Java泛型

为什么需要泛型

首先,我们来看一段代码:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		ArrayList animal = new ArrayList();

		animal.add(new Dog());
		animal.add(new Cat());

		Dog dog = (Dog) animal.get(0);
		Cat cat = (Cat) animal.get(1);

		Cat cat = (Dog) animal.get(0);//报错提示:Type mismatch: cannot convert from Dog to Cat
	}

}

不使用泛型带来的问题:
Dog 和 Cat 都是Animal的子类
ArrayList 默认接受Object类型的对象,所以所有对象都可以放进ArrayList中
所以get(0) 返回的类型是Object
接着,需要进行强制转换才可以得到 Dog 类型或者 Cat 类型。
如果软件开发人员记忆比较好,能记得哪个是哪个,还是可以的。 但是开发人员会犯错误,比如最后一行代码,会记错,把第0个对象转换为 Cat ,这样就会出现类型转换异常 ( java.lang.ClassCastException )

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。



什么是泛型?

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

使用泛型的好处:
泛型的用法是在容器后面添加<Type>
Type可以是类,抽象类,接口
泛型表示这种容器,只能存放Dog,Cat就放不进去了。

代码如下:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		   ArrayList<Dog> animal = new ArrayList<Dog>();
	         
	        //只有Dog可以放进去     
		   animal.add(new Dog());
	         
	        //Cat甚至放不进去
		   animal.add(new Cat());//报错提示:The method add(Dog) in the type ArrayList<Dog> is not applicable for the arguments (Cat)
	         
	        //获取的时候也不需要进行转型,因为取出来一定是Dog
	        Dog dog =  animal.get(0);
	}

}

设置泛型里存放的子类对象

 

假设容器的泛型是Animal,那么 Animal 的子类Dog,Cat都可以放进去
和Animal无关的类型Human还是放不进去

代码如下:

import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) {

		ArrayList<Animal> animal = new ArrayList<Animal>();

		// 只有作为animal的子类可以放进去
		animal.add(new Dog());
		animal.add(new Cat());

		// 和Animal无关的类型Human还是放不进去
		animal.add(new Human());
	    // 报错提示:The method add(Animal) in the type ArrayList<Animal> is not applicable for the arguments (Human)


	}

}

泛型的简写

 

为了不使编译器出现警告,需要前后都使用泛型,像这样:

ArrayList<Animal> animal= new ArrayList<Animal>();


不过JDK7提供了一个可以略微减少代码量的泛型简写方式

ArrayList< Animal > animal2 = new ArrayList<>();


后面的泛型可以用<>来代替

 

支持泛型的类

 

设计一个支持泛型的 GenericTest 类
设计这个类的时候,在类的声明上,加上一个<T>,表示该类支持泛型。
T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。

示例代码如下:

public class GenericTest<T> {
	ArrayList<T> objects = new ArrayList<T>(); 
	
	public void add(T t){
		objects.add(t);
	}

	public static void main(String[] args) {
		//在声明这个类的时候,使用泛型<Dog>就表示该类只能放Dog
		GenericTest<Dog> dog = new GenericTest<>();
		
		dog.add(new Dog());//✔
		
		dog.add(new Cat());//报错提示:The method add(Dog) in the type GenericTest<Dog> is not applicable for the arguments (Cat)

        //在声明这个类的时候,使用泛型<Animal>就表示该类只能放Animal,或者其子类
		GenericTest<Animal> animal = new GenericTest<>();
		
		animal.add(new Dog());//✔
        //不能放与Animal无关的Human类
		animal.add(new Human());//报错提示:The method add(Animal) in the type GenericTest<Animal> is not applicable for the arguments (Human)

	}

}

? extends

ArrayList animals<? extends Animal> 表示这是一个Animal 泛型或者其子类泛型
animals 的泛型可能是 Animal
animals 的泛型可能是 Dog
animals 的泛型可能是 Cat
所以 可以确凿的是,从 animals 取出来的对象,一定是可以转型成 Animal 的

但是,不能往里面放东西,因为
放 Dog  不满足 < Cat >
放 Cat   也不满足< Dog >

示例代码如下:

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<Cat> cats = new ArrayList<Cat>();
		cats.add(new Cat());

		ArrayList<? extends Animal> animals = cats;

		// ? extends Animal 表示这是一个Animal泛型的子类泛型

		// animals 的泛型可以使Animal
		// animals 的泛型可以使Cat
		// animals 的泛型可以使Dog

		// 可以确凿的是,从animals取出来的对象,一定是可以转型成Animal的

		Animal animal = animals.get(0);

		// 但是,不能往里面放东西
		animals.add(new Dog()); // 编译错误,因为 animals 的泛型 有可能是Cat
		//错误提示:The method add(capture#2-of ? extends Animal) in the type ArrayList<capture#2-of ? extends Animal> is not applicable for the arguments (Dog)
	}

}

? super

ArrayList animals<? super Animal> 表示这是一个Animal泛型或者其父类泛型
Animal 的泛型可能是 Animal
Animal 的泛型可能是 Object

可以往里面插入 Animal 以及 Animal 的子类
但是取出来有风险,因为不确定取出来是 Animal 还是Object

示例代码如下:

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<? super Animal> animals = new ArrayList<Object>();
        
        //? super Animal 表示 animals的泛型是Animal或者其父类泛型
          
        //animals 的泛型可以是Animal
        //animals 的泛型可以是Object
          
        //所以就可以插入Animal
		animals.add(new Animal());
        //也可以插入Animal的子类
		animals.add(new Cat());
		animals.add(new Dog());
          
        //但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Animal会失败
        Animal animal= animals.get(0);//报错提示:	Type mismatch: cannot convert from capture#4-of ? super Animal to Animal
          
    }
}

泛型通配符 ?

泛型通配符? 代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能

所以只能以Object的形式取出来
并且不能往里面放对象,因为不知道到底是一个什么泛型的容器

import java.util.ArrayList;

public class GenericTest<T> {

	public static void main(String[] args) {
		ArrayList<Cat> cats = new ArrayList<Cat>();

		// ?泛型通配符,表示任意泛型
		ArrayList<?> generalList = cats;

		// ?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
		// 所以只能以Object的形式取出来
		Object o = generalList.get(0);

		// ?的缺陷2: 既然?代表任意泛型,那么既有可能是Animal,也有可能是Human
		// 所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
		generalList.add(new Human()); // 编译错误 因为?代表任意泛型,很有可能不是Human
		generalList.add(new Animal()); // 编译错误 因为?代表任意泛型,很有可能不是Animal
		generalList.add(new Cat()); // 编译错误 因为?代表任意泛型,很有可能不是Cat

	}
}

总结

如果希望只取出,不插入,就使用? extends
如果希望只插入,不取出,就使用? super
如果希望,又能插入,又能取出,就不要用通配符?

 

PS:
子类泛型不可以转换为父类泛型

父类泛型不可以转型为子类泛型

转载于:https://my.oschina.net/u/3425197/blog/1143106

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值