定义带泛型的接口和类
来个栗子:
public class Tree<T>
{
//使用T类型形参定义属性
private T something;
public Tree(){}
//下面方法中使用T类型形参来定义方法
public Tree(T info)
{
this.something = info;
}
public T getSomething() {
return something;
}
public void setSomething(T something) {
this.something = something;
}
public static void main(String[] args)
{
//因为传给T形参的是String实际类型,所以构造器的参数只能是String
Tree<String> t1 = new Tree<String>("SycamoreTree");
System.out.println(t1.getSomething());
//因为传给T形参的是Double实际类型,所以构造器的参数只能是Double或double
Tree<Double> t2 = new Tree<Double>(17.26);
System.out.println(t2.getSomething());
}
}
继承带泛型类
public class AppleTree extends Tree<String>//可以
/*这样子类覆写父类方法,以前用到形参T处皆应换成String
*/
public class AppleTree extends Tree //可以,会有警告
public class AppleTree extends Tree<T> //不可以
并不存在泛型类
new ArrayList<String>().getClass()==new ArrayList<Integer>.getClass() //返回true
- 类的静态成员会在所有实例间共享,所以在静态成员的声明和初始化中不允许用类型形参
- instanceof 运算符不可用于带泛型类
如果 Foo 是 Bar 的子类(或子接口),而 G 是具有泛型声明的类或接口
则 Foo[]
仍是 Bar[]
的子类,G<Foo>
不是 G<Bar>
的子类
类型通配符
List<?>
表示各种泛型List的父类,但不能把元素加到List<?>
中
设定类型形参上限
List<? extends Number>
则传入的类型实参只能是Number或其子类
一种极端:可为类型形参设多个上限(至多一个父类上限,可多个接口上限,且父类上限须置于首位,与普通类单继承和多实现语法一致)
设定类型形参下限
List<? super Cat>
则传入的类型实参须为Cat类或其父类
当限定了方法的参数类型上限,则只能取,不能放
若想放,用super代替extends,此时只能放不能取
泛型方法
泛型方法格式
//仅多了泛型声明<T,S>
修饰符 <T,S> 返回值类型 方法名(形参列表){
方法体;
}
与接口、类声明中定义类型形参不同,类或接口的类型形参可在整个类或接口中用,而方法声明中定义的类型形参只能在方法中用,而方法中的泛型参数无须向使用类或接口一样要显式传入实际类型实参(List<String>
类名后加实参),方法名后不用加<String>
大多数时候可用泛型方法代替类型通配符
泛型方法允许类型形参被用来表示方法的一个或多个参数键的类型依赖关系,或方法返回值与参数间的类型依赖关系,如果没有,则应用通配符
如果某个方法中一个形参 a 的类型或返回值类型依赖于另一形参 b 的类型,则形参 b 的类型声明不应用通配符——因为形参 a 或返回值类型依赖于形参 b 的类型,如果形参 b 的类型无法确定,程序无法定义形参 a 的类型,这种情况下只能用泛型方法
也可两者结合用
class Collections{
public static <T,S extends T> void copy(List<T> b,List<S> a){...;}
类型形参 S 只使用一次,可换成:
class Collections{
public static <T> void copy(List<T> b,List<? extends T> a){...;}
}
泛型限制
- 基本类型不能用于类型参数,要用包装类Integer、Double……
- instanceof检查和类型转换只对原始类型(类型擦除后进行)
- static成员不可用类的类型变量
- 参数化类型的数组的实例化非法
- 不能创建泛型类型实例
- 不能创建泛型数组
T obj = new T();//不行
T[] arr = new T[10];//不行