目录
泛型在Java中是语法糖
语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。Java中最常用的语法糖主要有泛型、可变参数、条件编译、自动拆装箱、内部类等。虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖。
Java泛型的主要目的就是为了便于开发者写代码时限制传入参数的类型,在编译时期就进行提示强制设置传入的参数类型。用于避免绝大多数的类型转换异常。
泛型使用
泛型类
public class Generics<T> {
private T var;
public void setVar(T var){
this.var = var;
}
public T getVar(){
return var;
}
public static void main(String[] args) {
// 在声明前就指定需要传入的类型 不声明类型就默认示Object
Generics generics = new Generics();
// 指定传入的类型必须是引用数据类型
Generics<String> genericsTwo= new Generics<>();
genericsTwo.setVar("aaa");
}
}
占位标识
看了上面的栗子可能比较懵 怎么就多了个<T>呢 其实我们用<>来标识这是一个泛型类 写在类名后面。而T则是占位标识。这个占位标识可以随意取名只要符合命名规范即可。
比如
class A<sdadad>{
private sdadad var;
}
但是见明知意嘛,我们也不能太随意 一般我们常用的表示方式有
T Type
K V Key Value
E Element
V ViewBinding M ViewModel
注意传入的参数只能引用数据类型
基本数据类型是不能作为指定的泛型参数的
泛型接口
interface Comparator<T>{
void compare(T t);
T compare();
}
泛型方法
在方法的返回值前指定标识位
class Method{
public <T> void test(T t){
System.out.println(t.getClass().toString());
}
public static void main(String[] args) {
Method method = new Method();
method.test("有什么意义呢?对比直接使用Object。答:没什么意义");
}
}
显然当我们不涉及类型操作时,泛型有什么意义呢?没有意义!
如何才显得泛型方法有意义呢?使用泛型可以避免类型转换的问题。先来一个小小的对比。
但是下面的通配符对传入的参数进行限制进行才是泛型受人喜爱的原因。
class Method{
// 这样才有一点意义
public <T> T test2(T t){
return t;
}
public Object test3(Object object){
return object;
}
public static void main(String[] args) {
Method method = new Method();
// 使用泛型可以避免类型转换的问题
String str = method.test2("ssss");
String strTwo = (String)method.test3("sdadas");
}
// 泛型支持可变参数
public <T> void test(T... t){}
// 或者限定对传入参数的类型进行限制才有意义
}
通配符
类型通配符
顾名思义就是匹配任意类型的类型实参。
用 ? 来表示<?>
简单讲就是传什么都行
class WildCard<T>{
private T var;
public void setVar(T var){
this.var = var;
}
public T getVar(){
return var;
}
public static void main(String[] args) {
WildCard<?> wildCard = new WildCard<>();
WildCard<Object> wildCard1 = new WildCard<>();
// 有什么用呢?对比于Object 显然这样使用和Object没两样
// 怎么才有用呢 配合 extends/super
}
}
带限通配符
但是刚才讲了,传啥都行有个啥子意思勒。没啥子用。因此我们要限制所以就有了上下限的说法。
class WildCard<T>{
private T var;
public void setVar(T var){
this.var = var;
}
public T getVar(){
return var;
}
public static void main(String[] args) {
WildCard<?> wildCard = new WildCard<>();
WildCard<Object> wildCard1 = new WildCard<>();
// 有什么用呢?对比于Object 显然这样使用和Object没两样
// 怎么才有用呢 配合 extends/super
wildCard.toClass(new ArrayList());
wildCard.toClass(new LinkedList());
}
// 此时即可限定传入的类型必须是实现List接口的类 当然你也可以在对象声明时进行限制
public <T extends List > void toClass(T temp){
System.out.println(temp.getClass());
}
}
为什么没写一个super的案例呢?因为这样写不了
T extends Number 我不知道T是什么类型,但肯定是Number的子类,对于方法来说这可以父类类型接收子类对象。
T super Number 我不知道T是啥,但Number肯定是T的子类,对于方法来说,T是什么?大的转成小的需要强制类型转换的!!我不能自动给你处理强转
不理解看官方
那做什么用呢?看下面
上限通配符extend 取数据
extend 指定的类必须是继承某个类,或者实现了某个接口
? extend List 限定传入的类型必须是实现List接口的类 或者是List本身
List<? extends Animal> list = new ArrayList<Cat>();
只能取数据 保证取得的数据都是Animal可以接受的
下限通配符super 添数据
super 指定的类必须是自身 或者是其父类
? super List 限定传入的类型必须是List本身 或者是List的父类
List<? super Cat> list = new ArrayList<Animal>()
可以加入数据保证数据都是Cat 或者Cat的父类 Animal
这是Java Collections的copy方法源码 可以解释一下只能取和只能存的的意义各位读者自行感悟
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
泛型擦除
开篇就讲过泛型是一种语法糖,因此泛型是不会跑到Java虚拟机中的。那么如何做到让虚拟机正常识别呢?那就是泛型擦除。
有同学要问为什么不直接让虚拟机可以识别呢?泛型是JDK1.5才有的机制,为了向下兼容因此不能够直接在运行时就搞泛型。
怎么擦除呢?看个例子就好了
你写的代码
public class Demo<T> {
private T t;
public static void main(String[] args) {
}
public T getT(){
return t;
}
public void setT(T t){
this.t = t;
}
}
擦除后跑在虚拟机上的代码
public class Demo {
private Object t;
public static void main(String[] args) {
}
public Object getT(){
return t;
}
public void setT(Object t){
this.t = t;
}
}
当然通配符也会被擦除掉
<? extends T>
擦到T
<? super T> 擦到Object