其他
3 分钟带你彻底搞懂 Java 泛型背后的秘密
点击上方 Java后端,选择 设为星标
优质文章,及时送达作者 | 的一幕
来源 | www.jianshu.com/p/dd34211f2565
这一节主要讲的内容是java中泛型的应用,通过该篇让大家更好地理解泛型,以及面试中经常说的泛型类型擦除是什么概念,今天就带着这几个问题一起看下:
从这里可以看出来,不定义泛型也是可以往集合中添加数据的,所以说 泛型只是一种类型的规范,在代码编写阶段起一种限制。
下面我们通过例子来介绍泛型背后数据是什么类型
public class BaseBean<T> {
T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
常见的泛型主要有作用在普通类上面, 作用在抽象类、接口、静态或非静态方法上。
比如实际项目中,我们经常会遇到服务端返回的接口中都有 errMsg
、status
等公共返回信息,而变动的数据结构是data信息,因此我们可以抽取公共的BaseBean
:
public class BaseBean<T> {
public String errMsg;
public T data;
public int status;
}
抽象类或接口上的泛型
//抽象类泛型
public abstract class BaseAdapter<T> {
List<T> DATAS;
}
//接口泛型
public interface Factory<T> {
T create();
}
//方法泛型
public static <T> T getData() {
return null;
}
多元泛型
public interface Base<K, V> {
void setKey(K k);
V getValue();
}
泛型二级抽象类或接口
public interface BaseCommon<K extends Common1, V> extends Base<K, V> {
}
//或抽象类
public abstract class BaseCommon<K extends Common1, V> implements Base<K, V> {
}
抽象里面包含抽象
public interface Base<K, V> {
// void setKey(K k);
//
// V getValue();
void addNode(Map<K, V> map);
Map<K, V> getNode(int index);
}
public abstract class BaseCommon<K, V> implements Base<K, V> {
//多重泛型
LinkedList<Map<K, V>> DATAS = new LinkedList<>();
@Override
public void addNode(Map<K, V> map) {
DATAS.addLast(map);
}
@Override
public Map<K, V> getNode(int index) {
return DATAS.get(index);
}
}
<?>通配符
和<T>
区别是<?>在你不知道泛型类型的时候,可以用<?>通配符来定义,下面通过一个例子来看看<?>的用处:
//定义了一个普通类
public class BaseBean<T> {
T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
//用来定义泛型的
public class Common1 extends Common {
}
public static void main(String[] args) {
BaseBean<Common> commonBaseBean = new BaseBean<>();
//通配符定义就没有问题
BaseBean<?> common1BaseBean = commonBaseBean;
try {
//通过反射猜测setValue的参数是Object类型的
Method setValue = common1BaseBean.getClass().getDeclaredMethod("setValue", Object.class);
setValue.invoke(common1BaseBean, "123");
Object value = common1BaseBean.getValue();
System.out.println("result:" + value);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void setClass(Class<?> class){
//todo
}
<T extends **>表示上限泛型、<T super **>表示下限泛型
为了演示这两个通配符的作用,增加了一个类:
//新增加的一个BaseCommon
public class Common extends BaseCommon{
}
在BaseBean里面定义了个方法:
public void add(Class<? super Common> clazz) {
}
可以看到当传进去的是Common1.class的时候是不合法的,因为在add方法中需要传入Common父类的字节码对象,而Common1是继承自Common,所以直接不合法。
在实际开发中其实知道什么时候定义什么类型的泛型就ok,在mvp实际案例中泛型用得比较广泛,大家可以根据实际项目来找找泛型的感觉,只是面试的时候需要理解类型擦除是针对谁而言的。
类型擦除
其实在开篇的时候已经通过例子说明了,通过反射绕开泛型的定义,也说明了类中定义的泛型最终是以Object被jvm执行。所有的泛型在jvm中执行的时候,都是以Object对象存在的,加泛型只是为了一种代码的规范,避免了开发过程中再次强转。
泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
【END】