泛型
作用:解决类型安全问题, 使得模糊的Object类型变得精确. 使用更安全更简单
泛型的声明:
interface List<T> 和 class TestGen<K,V>
其中,T,K,V不代表值,而是表示类型。
这里使用任意字母都可以。常用T表示,是Type的缩写。
泛型的实例化:
一定要在类名后面指定类型参数的值(类型)。如: List<String> strList = new ArrayList<String>(); Iterator<Customer> iterator = customers.iterator(); T只能是类,不能用基本数据类型填充。
运用:在集合中使用泛型、自定义泛型类、泛型方法、泛型接口
class Person<T>{
//使用T类型定义变量
private T info;
//使用T类型定义一般方法
public T getInfo(){
return info;
}
public void setInfo(T info){
this.info = info;
}
//使用T类型定义构造器
public Person(){}
public Person(T info){
this.info = info;
}
/** 静态环境中不允许使用泛型类型.
public static void test(A a) {
System.out.println(a);
}*/
//不能在try-catch中使用泛型定义
//try{}
//catch(T t){}
}
对于泛型类
1.对象实例化时不指定泛型,默认为:Object。 2.泛型不同的引用不能相互赋值。 3.加入集合中的对象类型必须与指定的泛型类型一致。
4.静态方法中不能使用类的泛型。 5.如果泛型类是一个接口或抽象类,则不可创建泛型 类的对象。 6.不能在catch中使用泛型 7.从泛型类派生子类,泛型类型可以具体化
自定义泛型类
在类名后面<泛型类型参数>自定义泛型类中的
泛型信息, 本质上是隶属于对象的, 也称为成员泛型
class Person<A> { // A代表某种未知类类型, 具体是什么不知道, 即使不知道具体类型也能用.
// A这个类型 可以在本类中当成类型来用, 类型可以声明变量, 可以作为方法的返回值类型, 可以作为参数的类型.
// A类型会在使用者创建对象或声明引用时指定
private String name;
private A info;
public Person() {}
public Person(String name, A info) {
this.name = name;
this.info = info;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public A getInfo() {return info;}
public void setInfo(A info) {this.info = info;}
Person<String> p1 = new Person<String>("张三", "男");
String info1 = p1.getInfo();
//p1.setInfo(40); p1对象的内部的泛型指定成String, 类型安全了, 再设置成Integer就会报错
Person<Integer> p2 = new Person<Integer>("李四", 40);
Integer info2 = p2.getInfo();
//p2.setInfo("abc"); p2对象的内部泛型是Integer, 设置新的String值就出错
Person p3 = new Person("王五", true); // 在使用类时没有泛型, 最不济它就是Object类型
Object info3 = p3.getInfo();
p3.setInfo(3.22); // 类型不安全!!!
泛型类在继承中的处理
class Base<B> {
private B b;
public B getB() {return b;}
public void setB(B b) {this.b = b;}
}
class Sub1 extends Base {} // 子类在继承泛型父类时忽略了泛型信息, 在子类中B类型就是Object类型.
class Sub2 extends Base<String> {} // 子类在继承泛型父类时把泛型类型写死, 在子类中B类型永远是String
class Sub3 extends Base<Boolean> {} // 子类在继承泛型父类时把泛型类型写死, 在子类中B类型永远是Boolean
class Sub4<B> extends Base<B> {} // 子类在继承泛型父类时把泛型类型继承在子类仍然泛型, 在子类中B类型也是变化的
//测试方法
Base<String> base1 = new Base<String>();
String b = base1.getB();
Sub1 sub1 = new Sub1();
Object b1 = sub1.getB();
Sub2 sub2 = new Sub2();
String b2 = sub2.getB();
Sub3 sub3 = new Sub3();
Boolean b3 = sub3.getB();
Sub4<Float> floatSub4 = new Sub4<Float>();
Float b4 = floatSub4.getB();
Sub4<Double> doubleSub4 = new Sub4<Double>();
Double b5 = doubleSub4.getB();
对于泛型方法
泛型隶属于方法, 也称为局部泛型. 在方法返回值左面 <泛型类型>
泛型方法的格式:
[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
public <E> E get(int id, E e){
E result = null;
return result;
}}
泛型通配符 重点、需要再整理
常用的 T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。
通常情况下,T,E,K,V,?是这样约定的:
-
?表示不确定的 java 类型
-
T (type) 表示具体的一个java类型
-
K V (key value) 分别代表java键值中的Key Value
-
E (element) 代表Element
? 无界通配符
先从一个小例子看起,原文在 这里 。
我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表,我的第一个想法是像这样的:
List<Animal> listAnimals
但是老板的想法确实这样的:
List<? extends Animal> listAnimals
为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。
static int countLegs (List<? extends Animal > animals ) {
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
}
static int countLegs1 (List< Animal > animals ){
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
// 不会报错
countLegs( dogs );
// 报错
countLegs1(dogs);
}
当调用 countLegs1 时,就会飘红,提示的错误信息如下:
所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 ),表示可以持有任何类型。像 countLegs 方法中,限定了上届,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错。而 countLegs1 就不行。
上界通配符 < ? extends E>
上届:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:
-
如果传入的类型不是 E 或者 E 的子类,编译不成功
-
泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
private <K extends A, E extends B> E test(K arg1, E arg2){
E result = arg2;
arg2.compareTo(arg1);
//.....
return result;
}
类型参数列表中如果有多个类型参数上限,用逗号分开
下界通配符 < ? super E>
下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
private <T> void test(List<? super T> dst, List<T> src){
for (T t : src) {
dst.add(t);
}
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = new ArrayList<>();
new Test3().test(animals,dogs);
}
// Dog 是 Animal 的子类
class Dog extends Animal {
}
dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。
上界通配符主要用于读数据,下界通配符主要用于写数据。
浙公网安备 33010602011771号