《Java基础知识》Java 泛型详解
JDK 1.5 之后,Java 通过泛型解决了容器类型安全这一问题,而几乎所有人接触泛型也是通过Java的容器。那么泛型究竟是什么?
泛型的本质是参数化类型;也就是说,泛型就是将所操作的数据类型作为参数的一种语法。
先对比一下有泛型和无泛型的写法。
无泛型
public class Dog{ String name;
int age; /**
* 带参构造函数
* @param name
* @param age
*/
public Dog(String name,int age){
this.age = age;
this.name = name;
} public void ptint(){
System.out.println("name = "+this.name+";age = "+age);
}
}
public class Node {
private Object obj; public Object get(){
return obj;
} public void set(Object obj){
this.obj=obj;
} public static void main(String[] argv){
Dog dog=new Dog("花花",4);
Node node=new Node();
node.set(dog);
Dog dog2=(Dog)node.get();
dog2.ptint();
}
}
运行结果:
使用泛型
public class Node<T> {
private T obj; public T get(){
return obj;
} public void set(T obj){
this.obj=obj;
} public static void main(String[] argv){
Dog dog=new Dog("花花",4);
Node<Dog> node=new Node();
node.set(dog);
Dog dog2=node.get();
dog2.ptint();
}
}
分析:
第一种:通过Object 类接收,最后使用的时候需要强制转换后才能正常使用,并且强转是否正确,只有在运行时才能知道是否正确。
第二种:通过 T 接收,在使用的使用<> 中指定本次使用具体类,可以很好的做到要用什么传什么,并且改写法,可以在编译的时候就发现类型不匹配问题。T接收,代表是Object类,所以可以传如任何对象。
在上述 Node 类中,我们假设需要使用使用传入类Dog的方法。显然obj.ptint() 是不行,obj能用只有Object的方法。那要如何使用呢?
思路是:让T不再代表Object,而是我指定的子类或者父类。
案例:
public class Node<T extends Dog> {
private T obj; public T get(){
return obj;
} public void set(T obj){
this.obj=obj;
} public void printNode(){
obj.ptint();
} public static void main(String[] argv){
Node<Dog> node=new Node<>();
node.set(new Dog("花花",4));
node.printNode();
}
}
运行结果:
到这里,已经感受到泛型的美好:那就是方便。
注意:泛型传递的时候是不变的。
案例:
public class Dog1 extends Dog {
/**
* 带参构造函数
*
* @param name
* @param age
*/
public Dog1(String name, int age) {
super(name, age);
}
}
public class PublicTest {
public static void main(String[] args) {
Node<Dog> node0 = new Node<>();
node0.set(new Dog("花花",4));
// Node<Dog1> node0 = new Node<>(); 是会编译报错的
main1(node0);
} public static void main1(Node<Dog> node) {
node.printNode();
}
}
运行结果:
如果将上述代码的:Node<Dog> node0 = new Node<>(); 改成被注释的代码。会编译报错。也就是泛型是Node<Dog>只能传 Node<Dog>, 计算Dog1是Dog的子类也不行。上述写死的方式实在难受,所以Java也提供了解决方式。
案例:
public static void main(String[] args) {
Node<Dog1> node0 = new Node<>(); //不会编译报错,并且可以正常执行
node0.set(new Dog1("花花",6));
main1(node0);
} public static void main1(Node<? extends Dog> node) {
node.printNode();
}
运行结果:
上述案例中mian1方法的入参:Node<? extends Dog> node 。这样就允许传入子类了。
这个时候我们再考虑一下,既然有向下泛型,有没有向上泛型呢,答案是有的:mian1方法的入参:Node<? extends Dog> node 中的extends 改成supper就可以了。
这个案例,大家自己动手试试。
案例:
public class Node<T> {
private T obj; public T get(){
return obj;
} public void set(T obj){
this.obj=obj;
} public void printNode(){
System.out.println("只能使用Object方法");
}
}
public class PublicTest {
public static void main(String[] args) {
Node<Dog1> node0 = new Node<>(); //不会编译报错,并且可以正常执行
node0.set(new Dog1("花花",6));
main1(node0);
} public static void main1(Node<?> node) {
node.printNode();
}
}
运行结果:
上述案例:支持传入向上泛型和向下泛型,不建议使用。 因为这类泛型回到一个问题,Node<T> 类中只能使用Object 的几个方法,局限很大。
泛型使用中的注意点。
案例:
import java.util.concurrent.ThreadLocalRandom; public class PublicTest {
static <T> T[] toArray(T... args) {
return args;
} static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // Can't get here
} public static void main(String[] args) {
String[] attributes = pickTwo("Good", "Fast", "Cheap");
}
}
运行结果:
分析:上述代码第一感觉返回回来的是一个String[] 数组才对,为什么会出现类型转换错误。文章中已经提到:T 会被表示成Object。所以返回回来的数组Java也会处理成Object[] ,Object[] 是不能转换成 String[] 的。
既然知道原因了:我们试着修改一下代码:将T继承到String。这样java返回数组就会处理成String[]。
import java.util.concurrent.ThreadLocalRandom; public class PublicTest {
static <T extends String> T[] toArray(T... args) {
return args;
} static <T extends String> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // Can't get here
} public static void main(String[] args) {
String[] attributes = pickTwo("Good", "Fast", "Cheap");
System.out.println(attributes[0]+" : "+attributes[1]);
}
}
运行结果:
基类劫持:
案例:
public interface Node<T> {
T play();
}
public class Dog implements Node<Integer> { @Override
public Integer play() {
return null;
}
}
public class Dog1 extends Dog implements Node<String> {
//...
}
编译报错:因为基类已经实现 Node<Integer> 那么子类也只能实现 Node<Integer>。
Java泛型中的标记符含义:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd、3rd、4th types 和T的作用一样。
总结:泛型给我们带来了方便,同时也要注意他的特点和用法。
参考资料:
https://www.cnblogs.com/dengchengchao/p/9717097.html
《Java基础知识》Java 泛型详解的更多相关文章
- java 泛型详解(普通泛型、 通配符、 泛型接口)
java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...
- java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...
- Java泛型详解(转)
文章转自 importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...
- 【转】java 泛型详解
java 泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 ...
- 【转载】Java泛型详解
[转载]http://www.importnew.com/24029.html 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考 ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java基础11:Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...
- java 泛型详解-绝对是对泛型方法讲解
Reference: http://blog.csdn.net/s10461/article/details/53941091 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模 ...
- Java泛型详解,史上最全图文详解!
泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它. 毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课. 一:泛型本质 Java 泛型(gen ...
- 笔记-java泛型详解
首先,先说明一下,java泛型文章的出处:http://www.cnblogs.com/lzq198754/p/5780426.html 作为学习笔记保存. 1.为什么需要泛型 泛型在Java中有很重 ...
随机推荐
- mac安装配置Tomcat
一.安装Tomcat 1.首先到官网下载Tomcat:https://tomcat.apache.org/download-90.cgi 2.解压tomcat文件,最好把它文件名重命名为"T ...
- react antd Table动态合并单元格
示例数据 原始数组 const data = [ { key: '0', name: 'John Brown', age:22, address: 'New York No. 1 Lake Park' ...
- 都是为了生活组——‘’都是为了吃饭”微信小程序评价
基于NABCD评论作品,及改进建议 1.根据NABCD评论,作品的选题 N(Need,需求) 纠结症是目前在年轻人身上普遍存在着的问题,食堂食物众多,每次在吃饭前都要纠结好久,大大浪费了时间,还容易产 ...
- 转。http,状态码详解
转自konglingbinHTTP状态码详解:https://www.cnblogs.com/klb561/p/9205867.html HTTP状态码(HTTP Status Code)是用以表示网 ...
- 【CPLUSOJ】【USACO】【差分约束】排队(layout)
[题目描述] Robin喜欢将他的奶牛们排成一队.假设他有N头奶牛,编号为1至N.这些奶牛按照编号大小排列,并且由于它们都很想早点吃饭,于是就很可能出现多头奶牛挤在同一位置的情况(也就是说,如果我们认 ...
- 10分钟学会Python函数基础知识
看完本文大概需要8分钟,看完后,仔细看下代码,认真回一下,函数基本知识就OK了.最好还是把代码敲一下. 一.函数基础 简单地说,一个函数就是一组Python语句的组合,它们可以在程序中运行一次或多次运 ...
- 感觉async await 异步编程 并不能提升性能?
我有2个方法 代码相同 都是执行上传文件IO操作 一个同步 一个异步 接着我用POSTMAN 分别用200个线程 去同时测试2个接口 结果很意外 2个接口 同时执行完成的速度 异步更慢 之前经常看别人 ...
- mysql 安装报错集合
mysql-5.6.39 源码编译安装报错 报错信息: /tmp/ccV858jD.s: Assembler messages: /tmp/ccV858jD.s: Fatal error: can't ...
- 第二周选做(myod)
02.第二周myod(选做) 实验要求: 复习c文件处理内容 编写myod.c 用myod XXX实现Linux下od -tx -tc XXX的功能 main与其他分开,制作静态库和动态库 编写Mak ...
- Java基础IO类之字符串流(查字符串中的单词数量)与管道流
一.字符串流 定义:字符串流(StringReader),以一个字符为数据源,来构造一个字符流. 作用:在Web开发中,我们经常要从服务器上获取数据,数据返回的格式通常一个字符串(XML.JSON), ...