9.1 深入泛型
泛型类型具体化的时候必须是基本类型中的包装类,例如:Integer、Double。int不行
9.1.1 泛型接口
(1)泛型接口概念:就是在定义接口的时候,在接口名后面加上<泛型类型,泛型类型...>
interface genericityInterface<K>{
public K function();
}
(2)定义一个类实现该泛型接口:在实现泛型接口的时候,接口中的泛型也要传入具体的类型。如String,而在接口中用到泛型K的都需要在实现类中用具体类型String替代
public class infoClass implements genericityInterface<String>{
@Override
public String function() {
}
}
(3)实例:
/**
* 泛型接口
* @param <K> 泛型类型
*/
interface genericityInterface<K>{
/**
* 泛型的方法
* @return 返回泛型接口中对应的泛型类型
*/
public K function();
}
/**
* 实现泛型接口的类
* 在实现泛型接口的时候,接口中的泛型也要传入具体的类型,如String
*/
public class infoClass implements genericityInterface< String >{
private String[] study = new String[]{ "iphone", "Android","PHP" };
/**
* 重写接口的泛型返回值的抽象方法,类型也与对应泛型一致
*/
@Override
public String function() {
Random rand = new Random();
//取0-2随机数,(nextInt(3)是包括0,不包括3)
int randNum = rand.nextInt( 3 );
return study[randNum];
}
/**
* 主方法,调用该infoClass类
* @param args
*/
public static void main(String[] args) {
infoClass genClass = new infoClass();
System.out.println( genClass.function());
System.out.println( genClass.function());
System.out.println( genClass.function());
}
}
9.1.2 泛型类
(1)泛型类概念:定义类的时候,在类名后面加上<泛型类型,泛型类型...>
public class genericityClass<K, T> {
//定义一个泛型类型的属性key
private K key;
//定义一个泛型类型的属性temp
private T temp;
}
(2)实例该类的时候,指定泛型的具体类型:在编译期,是无法知道K和T具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。
genericityClass<Double,Integer> gClass2 = new genericityClass<>(25.0,1314);
System.out.println(gClass2.getKey()+" "+gClass2.getTemp());
(3)例子:
/**
* 泛型类genericityClass<K, T> 。K、T分别为两个泛型类型
*/
public class genericityClass<K, T> {
private K key;
private T temp;
//构造方法,传入两参数
public genericityClass(K key, T temp) {
this.key = key;
this.temp = temp;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public T getTemp() {
return temp;
}
public void setTemp(T temp) {
this.temp = temp;
}
/**
* 主方法
* 在编译期,是无法知道K和T具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。可
* 以看一下现在genericityClass类对于不同类型的支持情况
*/
public static void main(String[] args) {
genericityClass<String,String> gClass1 = new genericityClass<>("字符串1","字符串2");
genericityClass<Double,Integer> gClass2 = new genericityClass<>(25.0,1314);
genericityClass<Integer,Integer> gClass3 = new genericityClass<>(250,111111);
System.out.println(gClass1.getKey()+" "+gClass1.getTemp());
System.out.println(gClass2.getKey()+" "+gClass2.getTemp());
System.out.println(gClass3.getKey()+" "+gClass3.getTemp());
}
}
9.1.3 泛型方法
一个基本的原则是:无论何时,只要能做到,就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。
(1)泛型方法概念:定义方法时,在方法修饰符后加<泛型类型,泛型类型...>
public static<T> void methodOne(T t){ }
(2)调用该方法:传入指定的具体类型作为方法的参数,可以是任意类型。如果参数是泛型数组,传入的多个参数可以是不同类型
public static<T> void methodTwo(T... arr){
for(T t : arr){
System.out.println("methodTwo:"+t);
}
}
//方法二可以传入多个不同类型的参数
methodTwo("iphone",520,1314.0,'A');
(3)例子:
/**
* 泛型方法
*/
public class genericityMethod<K> {
//方法一传入的类型是Object都可以
public static<T> void methodOne(T t){
System.out.println("methodOne:"+t);
}
//方法二参数是一个泛型数组,传入多个参数的类型可以不相同
public static<T> void methodTwo(T... arr){
for(T t : arr){
System.out.println("methodTwo:"+t);
}
}
//方法三泛型类型的方法不能是静态,因为泛型还没具体化,不能静态
public <T> K methodThree( T t ){
K k = null;
System.out.println(t);
//返回泛型对象
return k;
}
//主方法
public static void main(String[] args) {
methodOne("Abdroid");
//方法二可以传入多个不同类型的参数
methodTwo("iphone",520,1314.0,'A');
//非静态的方法需要实例化本类
genericityMethod<String> genMethod = new genericityMethod<>();
//将返回的泛型对象输出
System.out.println(genMethod.methodThree( 33 ));
}
}
9.2 泛型通配符
9.2.1 类型通配符
泛型通配符:<?>:表示泛型的具体类型不唯一,可以是Integer、Double等等...
9.2.2 通配符上界、通配符下界、无界通配符
1.[通配符上界]
①“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”可以代表Animal或其子类,可代表不了Animal的父类
②对于通配符的上界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))
- [1] 如List<? extends Cat> 是 List<? extends Animal>的子类型)。
- [2] 如 List<? extends Animal>是List<Animal>的子类型)
- [3] List<?> 与List<? extends Objext>等同。
③不能往List<? extends Animal> 添加任意对象,除了null。
比如fruits.add(new Strawberry()),这里是add一个Strawberry类型对象,而fruits要求add的对象类型必须是Fruit或它的任何某种子类型对象,这个某种类型能用Strawberry或Fruit或Apple来表示吗?
④在List<? extends Animal> 可以是Animal类对象或Bird对象等(只是某一类对象),反过来说,在List<? extends Animal> list里的都是Animal对象,即Bird也是Animal对象,Cat也是Animal对象(用java的语言来说就是子类可以指向父类,父类却不能指向子类),那么在Animal里的所有方法都是可以调用的
2.[通配符下界]
①“? Super Cat”则表示通配符“?”的下界为Cat,换句话说就是,“? Super Cat”可以代表Cat或其父类,可代表不了Cat的子类
②对于通配符的下界,有以下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))
- [1]如List<? super Bird>是List<? super Animal>的子类型。
- [2]如List<Animal> 是 List<? super Animal>的子类型。
3.[无界通配符]
知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如List<?>,“?”可以代表任意类型,“任意”也就是未知类型。
[1]List<Object>与List<?>并不等同,List<Object>是List<?>的子类。还有不能往List<?> list里添加任意对象,除了null。
例子:
/**
* 泛型通配符 <?>
* @param <T>
*/ 泛型T继承了Number类才可以使用其中的doubleValue()等方法
public class AverageClass<T extends Number> {
T temp;
public T gettemp() {
return temp;
}
public void settemp(T temp) {
this.temp = temp;
}
//将输入的double转为int,方便比较
public int cast(){
return (int) temp.doubleValue();
}
//自定义的对比方法
public boolean equalsAver( AverageClass<?> aver ){
if( aver.cast()==this.cast()){
return true;
}else{
return false;
}
}
public static void main(String[] args) {
AverageClass<Integer> ave1 = new AverageClass<>();
//传入Integer类型
AverageClass<Integer> ave2 = new AverageClass<>();
//传入Double类型
AverageClass<Double> ave3 = new AverageClass<>();
ave1.settemp(123);
ave2.settemp(124);
ave3.settemp(123.0);
System.out.println(ave1.equalsAver(ave2)); //false
System.out.println(ave1.equalsAver(ave3)); //true
}
}
9.3 擦除和转换
9.3.1 擦除的概念
定义了一个List<Integer>对象li,赋给一个不带泛型的List变量list后,编译器 就会流失前者的泛型信息,这就是擦除。
9.3.2 擦除的例子
class Demo<T extends Number> {
//T类型的属性size
T size;
public Demo(){ }
public Demo(T size){
this.size = size;
}
public void setT(T size){
this.size = size;
}
public T getSize(){
return this.size;
}
}
class TestEraser{
public static void main(String[] args){
//method1();
method2();
//method3();
}
/**
* 把一个d赋给不带泛型信息的d2变量时,编译器会流失d对象的泛型信息,但因为Demo类型的参数类型
* 上限是Number类,所以编译器依然知道d2.getSize()的返回值类型是Number类型。
*/
public static void method1(){
Demo<Integer> d = new Demo<>(6);
Integer id = d.getSize();
//把d对象赋给Demo类型的变量,会丢失泛型信息
Demo d2 = d;
//编译错误,因为d2没了泛型。所以不能用Integer承接,只能用上限Number
//Integer id2 = d2.getSize();
Number id2 = d2.getSize();
}
/**
* 下面程序中定义了一个List<Integer>对象,当把对象li赋给一个不带泛型的List变量list后,编
* 译器 就会流失前者的泛型信息,这就是擦除。但java允许直接把List对象赋给一个List<Type>
*(Type可以是 任何类型)的变量。 从逻辑上看,List<String> 是list的子类,如果直接把一个
* List对象赋给一个List<String>对象 将引发编译错误,但对于泛型而言,可以直接把一个List对象
* 赋给一个List<String>对象,编译器 仅仅提示“未经检查的转换”,但要访问List<String>对象中
* 的元素将会引发ClassCastException异常
*/
public static void method2(){
List<Integer> li = new ArrayList<>();
li.add(6);
li.add(9);
List list = li;
//警告,编译运行完全正常
List<String> ls = list;
//但要访问ls集合里的元素,将会引起运行时异常
System.out.println(ls.get(0));
}
/**
* method2代码虽然可以编译通过,但实际上对list变量实际上引用的是List<Integer>集合,所以当
* 试图把集合里的元素当成String类型的对象取出时,将引发ClassCastException类型转换异常,上
* 面的代码 可以转换成下面的代码就好理解了。
*/
public static void method3(){
List li = new ArrayList();
li.add(6);
li.add(9);
//运行异常
System.out.println((String)li.get(0));
}
}
9.4 泛型与数组
①不能创建持有泛型的数组对象。
②不能创建泛型类型的数组: 擦除类型,转换为Object[]
③例子:
List<String>[ ] ts;
//编译出错,不能实例化泛型数组
ts = new List<>[10 ];
List[ ] list = new List[ ]( );
//不可以定义泛型数组,但可以转换
Ts = (List<String>[ ]) list;