Java知识点详解 4Java泛型详解

逆水行舟用力撑,一篙松劲退千寻。这篇文章主要讲述Java知识点详解 4Java泛型详解相关的知识,希望能为你提供帮助。
list.add(29);
for (int i = 0; i < list.size(); i++) {
String str = (String) list.get(i);
System.out.println(" 泛型测试,str = " + str);
}
}
}
2、控制台输出?

Java知识点详解 4Java泛型详解

文章图片

崩溃了。
然而为什么呢?
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
Java知识点详解 4Java泛型详解

文章图片

定义泛型之后,编译都通不过了,要的就是这个效果!
三、泛型的优缺点
========
1、优点
(1)类型安全
泛型的主要目的是提高java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在非常高的层次上验证类型假设。没有泛型,这些假设就只能存在于系统开发人员的头脑中。
通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误就可以在编译时被捕获了,而不是在运行时当作ClassCastException展示出来。将类型检查从运行时挪到编译时有助于Java开发人员更早、更容易地找到错误,并可提高程序的可靠性。
(2)消除强制类型转换
泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。尽管减少强制类型转换可以提高使用泛型类的代码的累赞程度,但是声明泛型变量时却会带来相应的累赞程度。在简单的程序中使用一次泛型变量不会降低代码累赞程度。但是对于多次使用泛型变量的大型程序来说,则可以累积起来降低累赞程度。所以泛型消除了强制类型转换之后,会使得代码加清晰和筒洁。
(3)更高的效率
在非泛型编程中,将筒单类型作为Object传递时会引起Boxing(装箱)和Unboxing(拆箱)操作,这两个过程都是具有很大开销的。引入泛型后,就不必进行Boxing和Unboxing操作了,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显。
(4)潜在的性能收益
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,Java系统开发人员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的JVM的优化带来可能。
四、常见泛型字母含义
==========
格式: 类名< 字母列表>
T Type表示类型
K? V? 分辨表示键值对中的key value
E 代表Element
?表示不确定的类型
五、使用泛型时的注意事项
============
1、在定义一个泛型类时,在“< > ”之间定义形式类型参数,例如:“class TestGen< K,V> ”,其中“K”,“V”不代表值,而是表示类型。
2、实例化泛型对象时,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。
3、使用泛型时,泛型类型必须为引用数据类型,不能为基本数据类型,Java中的普通方法,构造方法,静态方法中都可以使用泛型,方法使用泛型之前必须先对泛型进行声明,可以使用任意字母,一般都要大写。
4、不可以定义泛型数组。
5、在static方法中不可以使用泛型,泛型变量也不可以用static关键字来修饰。
6、根据同一个泛型类衍生出来的多个类之间没有任何关系,不可以互相赋值。
7、泛型只在编译器有效
Java知识点详解 4Java泛型详解

文章图片

8、instanceof不允许存在泛型参数
以下代码不能通过编译,原因一样,泛型类型被擦除了
Java知识点详解 4Java泛型详解

文章图片

六、泛型的使用
=======
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
1、泛型类
package javase.genericity;
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic< T> {
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
//泛型构造方法形参key的类型也为T,T的类型由外部指定
public Generic(T key){
this.key = key;
}
//泛型方法getKey的返回值类型为T,T的类型由外部指定
public T getKey(){
return key;
}
public static void main(String[] args) {
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic< Integer> genericInteger = new Generic< Integer> (123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic< String> genericString = new Generic< String> (" 江疏影" );
System.out.println(" 泛型测试,key is " +genericInteger.getKey());
System.out.println(" 泛型测试,key is " +genericString.getKey());
}
}
Java知识点详解 4Java泛型详解

文章图片

泛型参数就是随便传的意思!
Generic generic = new Generic(" 111111" );
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
2、泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口
public interface Generator< T> {
public T next();
}
当实现泛型接口的类,未传入泛型实参时:
/**
  • 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
  • 即:class FruitGenerator< T> implements Generator< T> {
  • 如果不声明泛型,如:class FruitGenerator implements Generator< T> ,编译器会报错:" Unknown class"
*/
class FruitGenerator< T> implements Generator< T> {
@Override
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
package javase.genericity;
import java.util.Random;
public class FruitGenerator implementsGenerator< String> {
String[] fruits = new String[]{" apple" ," banana" ," Pear" };
@Override
public String next() {
Random random = new Random();
System.out.println(fruits[random.nextInt(3)]);
return fruits[random.nextInt(3)];
}
public static void main(String[] args) {
FruitGenerator ff = new FruitGenerator();
ff.next();
}
}
Java知识点详解 4Java泛型详解

文章图片

3、泛型通配符
我们知道integer是number的一个子类,同时Generic< Integer> 和Generic< Number> 实际上是相同的一种基本类型。那么问题来了,在使用Generic< Number> 作为形参的方法中,能否使用Generic< Integer> 的实例传入呢?在逻辑上类似于Generic< Number> 和Generic< Integer> 是否可以看成具有父子关系的泛型类型呢?
为了弄清楚这个问题,我们使用Generator< T> 这个泛型类继续看下面的例子:
Java知识点详解 4Java泛型详解

文章图片

回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic< Integer> 类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic< Integer> 和Generic< Number> 父类的引用类型。由此类型通配符应运而生。
我们可以将上面的方法改一下:
Java知识点详解 4Java泛型详解

文章图片

类型通配符一般是使用?代替具体的类型参数,注意了,此处?是类型实参,而不是类型形参。此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是????;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
4、泛型方法
泛型类,是在实例化类的时候指明泛型的具体类型;
泛型方法,是在调用方法的时候指明泛型的具体类型。
package javase.genericity;
public class Test {
public static void main(String[] args) {
try {
Object CSDN = genericMethod(Class.forName(" javase.genericity.CSDN" ));
System.out.println(CSDN);
Object OSCHINA = genericMethod(Class.forName("
【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】浏览器打开:qq.cn.hn/FTf 开源分享

javase.genericity.Oschina" ));
System.out.println(OSCHINA);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
  • 泛型方法的基本介绍
  • @param tClass 传入的泛型实参
  • @return T 返回值为T类型
  • 说明:
  • 1)public 与 返回值中间< T> 非常重要,可以理解为声明此方法为泛型方法。
  • 2)只有声明了< T> 的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  • 3)< T> 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  • 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public static < T> T genericMethod(Class< T> tClass)throws InstantiationException,IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
}
Java知识点详解 4Java泛型详解

文章图片

  • 泛型方法与可变参数
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法?。
package javase.genericity;
public class GenericFruit {
//静态方法中使用泛型,必须要将泛型定义在方法上。
public static < T> void printMsg(T...args){
for(T t:args){
System.out.println(" 泛型测试,it is " +t);
}
}
public static void main(String[] args) {
printMsg(" 1111" ,2222," 江疏影" ," 0.00" ,55.55);
}
}
Java知识点详解 4Java泛型详解

文章图片

尽量使用泛型方法!
七、泛型上下边界
========
1、设定通配符上限
首先,我们来看一下设定通配符上限用在哪里....
现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???
我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限
Java知识点详解 4Java泛型详解

文章图片

2、设定通配符下限
既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧
//传递进来的只能是Type或Type的父类
< ? super Type>
设定通配符的下限这并不少见,在TreeSet集合中就有....我们来看一下
public TreeSet(Comparator< ? super E> comparator) {
this(new TreeMap< > (comparator));
}
那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet& lt; String& gt; 类型的变量的时候,并传入一个可以比较String大小的Comparator。
那么这个Comparator的选择就有很多了,它可以是Comparator& lt; String& gt; ,还可以是类型参数是String的父类,比如说Comparator& lt; Objcet& gt; ....
这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了
八、泛型擦除
======
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
九、兼容性
=====
JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。
当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。
值得注意的是:它保留的就类型参数的上限。
List< String> list = new ArrayList< > ();
//类型被擦除了,保留的是类型的上限,String的上限就是Object
List list1 = list;
它也不会报错,仅仅是提示“未经检查的转换”。
十、泛型的应用
=======
当我们写网页的时候,常常会有多个Dao,我们要写每次都要写好几个Dao,这样会有点麻烦。
Java知识点详解 4Java泛型详解

文章图片

那么我们想要的效果是什么呢??只写一个抽象Dao,别的Dao只要继承该抽象Dao,就有对应的方法了。
要实现这样的效果,肯定是要用到泛型的。因为在抽象Dao中,是不可能知道哪一个Dao会继承它自己,所以是不知道其具体的类型的。而泛型就是在创建的时候才指定其具体的类型。
1、抽象dao
package javase.genericity.dao;
import javase.genericity.entity.Worker;
import javax.jms.Session;
【Java知识点详解 4Java泛型详解】import java.lang.reflect.ParameterizedType;

    推荐阅读