在创建集合对象的时候,要保证前后的泛型一致,或者创建集合时与前面的范型保持某种关系,如果不一致或不存在关系在编译时就会出错。例如,下面这句就会编译出错:
List<Object> listTest = new ArrayList<String>();
为什么会编译出错呢?
这是因为ArrayList虽然是List的实现类,String是Object的实现类,但是ArrayList<String>
不是List<Object>
的子类或实现类,因此会发生编译错误。我们来模拟一下这个过程:
package com.yx.yzh.test;
public interface Test<T>{}
package com.yx.yzh.test;
public class Test4<T> implements Test<T>{
public static void main(String[] args) {}
}
我们看到,Test与Test4的范型都是T,因此在创建Test4的对象时,需要保持前后的范型的类型一致,否则会发生编译错误,例如,下面两种情况会发生编译错误:
Test<String> test = new Test4<Object>();
Test<Object> test2 = new Test4<String>();
需要保持一致:
Test<String> test3 = new Test4<String>();
我们再来看一下继承关系中的范型:
package com.yx.yzh.test;
public class Test1<E> {}
package com.yx.yzh.test;
public class Test5<T> extends Test1<T> {
public static void main(String[] args) {
//编译错误
//Test1<String> test = new Test5<Oject>();
//编译错误
//Test1<Object> test2 = new Test5<String>();
//正确
Test1<String> test = new Test5<String>();
}
}
但是,要注意当两种或以上的范型遇上时,需要将范型提到第一个类型的范型上,且其中之一的范型与实现类或继承类的范型保持一致:
package com.yx.yzh.test;
public class Test1<E> {}
package com.yx.yzh.test;
public class Test3<E, E2> extends Test1<E2> {
public static void main(String[] args) {
Test1<String> t = new Test3<Object,String>();
}
}
E2范型需要一一对应,因此Test1与Test3的第二范型保持一致,均为String类型,剩下的范型没有一一对应关系,因此可为任意类型。这里涉及到了一个概念:向上转型。向上转型是指父类或接口的引用指向子类或实现类。也就是说使用父类或接口来接收创建对象的引用。上面的例子就是向上转型。有向上转型自然也有向下转型。向下转型是指由于子类或实现类的引用不能直接指向父类或接口,需要强制转换(子类或实现类)才能使用子类或实现类来接收父类或接口。例如:
package com.yx.yzh.test;
public class Test3<E, E2> extends Test1<E2> {
public static void main(String[] args) {
//向上转型
Test1<String> t = new Test3<Object,String>();
//向下转型
Test3<Object,String> t2 = (Test3<Object,String>)t;
}
}
A、对于单字母类范型在java中约定:T表示类型(Type),E表示元素类型(Element)。
B、范型通配符?
泛型通配符?是指当有不明确类型的范型时,可以使用?来代替范型类型。需要注意的是,在创建类中需要明确指出某种类型,不能使用?范型通配符。因此?范型通配符只能使用在方法参数类型范型声明中或者接收引用的那一方:
例
package com.yx.yzh.test;
public class Test5<T> extends Test1<T> {
public void helloWorld(){}
public void show(Test5<?> t){}
}
package com.yx.yzh.test;
public class Test6<T> extends Test5<T>{
//不知道T声明est6是什么类型,但是创建Test6时必须明确给出类型
//Test6<?> t = new Test6<String>();
//Test5<?> t2 = new Test6<Integer>();
//方法中可以使用?范型通配符声明,在调用时需要明确给出引用的具体范型类型
public void show(Test5<?> t){
t = this;
t.helloWorld();
}
public void helloWorld(){
System.out.println("hello woold!");
}
public static void main(String[] args) {
Test5<?> t = new Test6<String>();
//t接受了具体范型类型
t.show(t);
}
}
C、? extends E
? extends E表示该范型的类型的向上限定,也就是具体类型必须为E及其子类。同理,? extends E只能使用在方法参数类型范型声明中或者接收引用的那一方。
例
已知Student继承自People类
package com.yx.yzh.test;
public class People {}
package com.yx.yzh.test;
public class Student extends People{}
在声明范型时,可以这样理解? extends E中的?通配符:
package com.yx.yzh.test;
public class Test8<T> extends Test7<T> {
public static void main(String[] args) {
//final类不能被继承,但? extends String这是个例外表达式
Test7<? extends String> t = new Test8<String>();
Test7<? extends Object> t2 = new Test8<String>();
//错误,因为?的父类为String,范型向上限定为String,所以?为Object会编译错误
//Test7<? extends String> t3 = new Test8<Object>();
//Student是People的子类,而Test7与Test8范型一致,因此?表示Student,正确,
Test7<? extends People> t4 = new Test8<Student>();
}
//传参时,传入的引用的范型需要是People或People的子类或是实现类
public void show(Test7<? extends People> t1){
}
}
在? extends E中?也可以是可以是实现类与接口的关系:
现已知ClassT是InterfaceT的实现类
package com.yx.yzh.test;
public interface InterfaceT {}
package com.yx.yzh.test;
public class ClassT implements InterfaceT{}
那么可以满足? extends E表达式为:
Test7<? extends InterfaceT> t5 = new Test8<ClassT>();
需要注意的是,在方法中声明类型时必须给出范型的具体类型,范型不能是E、T。例如,右边表达式会编译错误:Test7<? extends T> t5 = new Test8<ClassT>();
但是在方法中可以声明参数的范型为? extends E这种形式。
package com.yx.yzh.test;
public class Test8<T> extends Test7<T> {
public void show(Test7<? extends People> t1){}
//在调用时,需要给出T类型和?的具体类型,?就是相应的范型类型,且必须与T保持继承关系
public void show2(Test7<? extends T> t1){}
}
但是,当方法中的范型范型为? extends T,在传入参数时,前后必须明确给出范型类型,而不能使用? extends T进行声明,否则在调用时会发生编译错误:
package com.yx.yzh.test;
public class Test8<T> extends Test7<T> {
public static void main(String[] args) {
Test8<Student> t6 = new Test8<Student>();//明确类型
t6.show2(t6);
}
public void show2(Test8<? extends T> t1){}
}
//可以编译通过,但是调用方法时会报错:
Test8<? extends People> t7 = new Test8<Student>();
T7.show2(t7);
应明确指出类型:
Test8<Student> t6 = new Test8<Student>();//明确类型
t6.show2(t6);
D、? super E
?super E表示该范型的类型的向下限定,也就是具体类型必须为E及其父类。同理,?super E只能使用在方法参数类型范型声明中或者接收引用的那一方。
package com.yx.yzh.test;
public class Test8<T> extends Test7<T> {
public static void main(String[] args) {
//尽量不要这么声明,需要声明具体泛型类型
Test7<? extends People> t10 = new Test8<Student>();
Test8<Student> t8 = new Test8<Student>();
t8.show3(t8);//? super Student
Test8<People> t9 = new Test8<People>();
t9.show3(t9);//? super People
}
public void show3(Test8<? super T> t1){
}
}