> 文章列表 > 彻底理解java中泛型

彻底理解java中泛型

彻底理解java中泛型

一、什么是泛型?

  泛型,即“类型参数化”,说人话,就是将具体的类型定义成参数,在使用时传递具体类型。

public class Test<T> {public void test1(String params){}public void test2(T params){}
}

泛型类Test中方法test1形参定义为具体类型String,方法test2中形参为泛型,在具体进行传递。

泛型可以使用在类、接口、方法

二、为什么有泛型?

  我们都知道java中泛型又称为“伪泛型”,为什么称为伪泛型呢?是因为泛型只存在编译阶段,编译完成之后泛型会被擦除,并没有作用在程序的执行阶段,所以他存在的目的就是“为了规范”。

1、使用集合时规范,保证集合中存入的是同一类型的数据,在取数、类型转换时防止报错,在代码编写阶段发现问题。

2、抽象一类事物的公共行为、属性,使用泛型,提高代码的可复用性。

3、解决了元素不确定性。

三、泛型的使用

1、泛型类、泛型方法

public class Test<T> {public T test2(T params){}
}

2、泛型通配符

  比如常见的?,T,K,V 就是通配符,比如其中的T换成A-Z其中任何一个都可以,java中是讲究约定的,就是大家共同约定,某个字符代表什么意思,这样也有利于代码维护。

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

2、上界通配符(<? extends T>)、下界通配符(<? super T>)

 a、为什么会有这两通配符?

  开发人员在使用泛型的时候,犯一些错误很容易根据自己的直觉而。比如一个方法如果接收 List<Object> 作为形式参数,那么如果尝试将一个 List<String> 的对象作为实际参数传进去,却发现无法通过编译。

 虽然从直觉上来说,Object 是 String 的父类,这种类型转换应该是合理的。但是实际上这会产生隐含的类型转换问题,因此编译器直接就禁止这样的行为。

b、上界通配符使用

  比如有Fruit水果类,和它的派生类Apple

class Fruit {}
class Apple extends Fruit {}

有一个简单的容器:Plate类,盘子里可以放各类水果”,对盘子中水果做“”和“”的动作:set( )get( )方法。

class Plate<T>{private T item;public Plate(T t){item=t;}public void set(T t){item=t;}public T get(){return item;}
}

定义在水果盘中放苹果,就会出现编译报错

 问题:水果之间有继承关系,但是放水果的容器之间没有继承关系,所以这个时候我们要解决,容器都能放水果,所以边界通配符就出现了。

public class Test<T> {public static void main(String[] args) {Plate<? extends Fruit> plate = new Plate<>(new Apple());//不能存入任何元素plate.set(new Fruit());plate.set(new Apple());//读取出来的东西只能存放在Fruit或它的父类里Apple apple = plate.get(); //报错Fruit fruit = plate.get();Object obj = plate.get();}
}

 上界通配符又称为get原则,是指一个范围内,上边界的一个限制条件,只能get操作,是因为编译器知道容器里面存储的是Fruit及Fruit的子类,所以可以get出里面的元素。

  不能add操作,是因为编译器不知道具体那个子类。

C、下界通配符使用

  继续使用上面的例子,

//水果
class Fruit {}
//苹果
class Apple extends Fruit {}
//红苹果
class RedApple extends Apple{}
//绿苹果
class GreenApple extends Apple{}
class Plate<T>{private T item;public Plate(T t){item=t;}public void set(T t){item=t;}public T get(){return item;}
}

public class Test<T> {public static void main(String[] args) {Plate<? super Apple> plate = new Plate<>(new Apple());//存入元素正常plate.set(new GreenApple());plate.set(new Apple());Apple apple = plate.get(); //报错Fruit fruit = plate.get();}
}

 下界通配符又称为put原则,是指一个范围内,下边界的一个限制条件,只能add操作,是因为编译器知道容器泛型边界是Fruit及Fruit的父类的,所以容器中可以存储Fruit子类。

  • <? extends C> 适合大量做获取操作的情景。
  • <? super C> 适合大量做添加操作的情景。

特点:<? extends C> 的 add() 被限制,<? super C> 的 get() 被限制。

四、泛型常见面试问题 

1、泛型为什么不能使用基本数据类型

泛型在编译阶段会进行泛型擦除,擦除为原始类型,但是object并不是基本数据类型的父类。

2、类型擦除后保留的原始类型