导读
文章主要有三段
- 泛型类初始化介绍
- 通配符
- 擦除
如果你是急性子可以直接跳过本段
下面代码有#号标识处有三处语法错误,如果你对此没有任何疑问,那么本文可能无法带给你更多知识。
public class GenericTest {
static class A{}
static class B extends A{}
static class C{}
public static void main(String[] args) {
List none = new ArrayList();
List<A> listA = new ArrayList<>();
List<? extends A> listOextendsA = new ArrayList<>();
List<?> listO = new ArrayList<>();
ArrayList<?>[] a = new ArrayList<?>[5];
ArrayList<A>[] b = new ArrayList[5];
//使用测试
none.add(new B()); // #1
listA.add(new B()); // #2
listOextendsA.add(new B()); // #3
listO.add(new B()); // #4
//赋值测试
listOextendsA = new ArrayList<C>(); // #5
listOextendsA = new ArrayList<B>(); // #6
a[0] = new ArrayList<A>(); // #7
b[0] = new ArrayList<A>(); // #8
none = new ArrayList<Object>(); // #9
}
}
泛型类初始化介绍
泛型类声明可以分为五个档位。
- 直接声明不加任何修饰 List list
JAVA中不强制泛型类必须使用泛型,但是默认会给没有指定泛型声明的对象默认加上 < object > , 所以不声明等于声明为Object
//例子
List none = new ArrayList();
none.add("1"); // 正确
none.add(1); // 正确
List<Object> none1 = new ArrayList<>();
none1.add("1"); // 正确
none1.add(1); // 正确
- 直接指定类型 List< String > list
直接指明类型的泛型类, 只能使用指定类型,不能使用包括子类在内的任何类型
//例子 继代码片段一 B extends A
List<A> listA = new ArrayList<>();
listOextendsA.add(new B()); // 错误
listOextendsA.add(new A()); // 正确
- 范围指定 List< ? extends String> list;
?提供泛型占位,必须通过带有具体类型指明的声明才能使用。本例中 向下兼容,通过? 和 extends 指定范围,接受指定类与子类 的【初始化】
//例子 继代码片段一 B extends A
List<? extends A> listOextendsA = new ArrayList<>();
listOextendsA = new ArrayList<B>() // 正确
listOextendsA.add(new A()); // 错误
- 范围指定 List< ? super String> list;
?提供泛型占位,必须通过带有具体类型指明的声明才能使用。本例中 向上兼容,通过? 和 super 指定范围,接受指定类与父类 的【初始化】
//例子 继代码片段一 B extends A
List<? extends B> listOextendsA = new ArrayList<>();
listOextendsA = new ArrayList<A>() // 正确
listOextendsA.add(new A()); // 正确
- 通配符?指定 List< ?> list;
?提供泛型占位,必须通过带有具体类型指明的声明才能使用。 本例中?可以初始化任何类型。
//例子
List<? extends B> listOextendsA = new ArrayList<>();
listOextendsA.add(new A()); // 错误
listOextendsA = new ArrayList<A>() // 正确
listOextendsA.add(new A()); // 正确
listOextendsA = new ArrayList<B>() // 正确
listOextendsA.add(new A()); // 错误
通配符
什么是通配符
通配符“?”在泛型中有不可替代的特殊性。 通配符可以表示任何类型,但又不是任何一个具体类型。在 List< ? > list = new List< ? >(); 中,初始化了一个泛型List,但是编译器并不明确这是一个什么类型的泛型(不明确的类型,不等于new List() 因为直接声明会默认为Object类型),所以这个对象是不能直接使用的。 试想一下编译器声明了一个自己都不知道类型的泛型类,估计内心已经慌得一B。。
通配符的用处
1 做边界限定,在前面例子中已经使用。
2 泛型占位,声明我是一个泛型(没有具体类型的泛型), 在我们可能遇见场景有以下场景, 更多用法需要大家一起创造。
// 代码片段
interface TestA{
List<?> outList();
}
class TestB implements TestA{
@Override
public List<?> outList() {
return new ArrayList<String>();
}
}
class TestC implements TestA{
@Override
public List<?> outList() {
return new ArrayList<Integer>();
}
}
擦除
- 证明擦除的存在
不知道你们有没有发现一些问题比如:
# 1
public static void main(String[] args) {
System.out.println(new ArrayList<String>().getClass());
}
//输出结果 class java.util.ArrayList
# 2
public static void main(String[] args) {
System.out.println(new ArrayList<String>() instanceof ArrayList<Integer>());
}
// 语法错误
种种迹象表明擦除确实存在,并给我们带来一些困惑。擦除是JAVA语言实现泛型的折中之法,是JAVA团队在参考C++之后设计出的新特性, 而擦除则是为了兼容。
- 擦除会给我们带来什么
1,无法在泛型类中直接获取泛型的类型
2,在某些情况下泛型无法在编译器无法给予明确的类型警告(数组给出了鄙视)