一、泛型的好处
1、多种数据类型执行相同代码的时候,可以共用一段代码。
private void add(int x, int y) {
//do something
}
private void add(double x, double y) {
//do something
}
private <T> void add(T x, T y) {
//do something
}
2、使用了泛型的时候不需要强制转换。
List list = new ArrayList<>();
list.add("mark");
list.add("OK");
list.add(1);
for(int i=0;i<list.size();i++){
String name=list.get(i);
System.out.println(name);
}
像上边的代码,编译的时候没有任何问题,因为List定义的是泛型类型,但是运行的时候会报错
java.lang.ClassCastException:java.lang.Integer cannot to be cast java.lang.String
如果想正常运行,我们需要做强制类型转换String name=(String)list.get(i);
如果我们在使用的时候就传入数据类型,就避免了强制类型转换。
List<String> list = new ArrayList<>();
list.add("mark");
list.add("OK");
//这样再添加int型数据的时候,直接在编译阶段就报错了
list.add(1);
for(int i=0;i<list.size();i++){
//这里不再需要做强制类型转换
String name=list.get(i);
System.out.println(name);
}
二、泛型的几种类型
1、泛型类
public class GenericClass<T> {
private T data;
public GenericClass() {
}
public GenericClass(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args){
GenericClass<String> genericClass =new GenericClass<>();
genericClass.setData("OK");
System.out.println(genericClass.getData());
}
}
泛型可以有多个
public class GenericClass2<T,K> {
private T data;
private K result;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public K getResult() {
return result;
}
public void setResult(K result) {
this.result = result;
}
public static void main(String[] args) {
}
}
2、泛型接口
泛型也是可以有多个
public interface GenericInterface<T,K> {
public T next();
public K next2();
}
实现泛型接口,可以有两种方式
public class ImplGenericInterface1<T,K> implements GenericInterface<T,K> {
@Override
public T next() {
return null;
}
@Override
public K next2() {
return null;
}
}
public class ImplGenericInterface2 implements GenericInterface<String, Integer> {
@Override
public String next() {
return null;
}
@Override
public Integer next2() {
return null;
}
}
3、泛型方法
<T> 代表定义泛型方法。
public <T> T genericMethod(T a){
return a;
}
1)泛型方法是完全独立的,不必非要声明在泛型类或泛型接口中。
2)可以有多个泛型。
3)泛型类中定义的泛型方法参数类型以泛型方法中的为准。
public class ClassOne<T> {
//普通方法中必须用类中声明的泛型T
public void show1(T t){
System.out.println(t.toString());
}
//这里的T是一个全新的类型,可以与泛型类中声明的T不是同一个类型
public <T> void show2(T t){
System.out.println(t.toString());
}
//这个E类型可以与T相同,也可以不同
public <E> void show3(E e){
System.out.println(e.toString());
}
public static void main(String[] args) {
Apple apple=new Apple();
People people=new People();
ClassOne<Apple> classOne=new ClassOne<>();
classOne.show1(apple);
classOne.show2(apple);
classOne.show2(people);
classOne.show3(apple);
classOne.show3(people);
}
}
class Apple{
}
class People{
}
public class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
/**
* 虽然在这个方法中使用了泛型,但是这并不是一个泛型方法
*/
public T getKey() {
return key;
}
/**
* 这不是一个泛型方法,就是一个普通的方法,
* 只是使用了Generic<T>这个泛型类做形参而已
*/
public void show(Generic<T> obj) {
}
/**
* 这个方法是有问题的,因为在类的声明中并未声明泛型E,
* 所以在使用E作为形参和返回值类型时,编译器会无法识别。
*/
public E setKey(E key) {
}
/**
* 这个方法是有问题的,这虽然是一个泛型方法,
* 但是只声明了泛型类型T,并未声明泛型类型E,
* 因此编译器并不知道该如何处理E这个类型
*/
public <T> T show(Generic<E> ab) {
return key;
}
/**
* 在泛型类中声明了一个泛型方法,使用泛型E,
* 泛型E可以是任意类型,可以与泛型T相同,也可以不同
*/
public <E> void show2(E t) {
System.out.println(t.toString());
}
/**
* 在泛型类中声明了一个泛型方法,使用泛型T,
* 这个T是一种全新的类型,可以与泛型类中的T相同,也可不同
*/
public <T> void show3(T t) {
System.out.println(t.toString());
}
/**
* 可以在泛型方法中使用泛型类中的泛型
*/
public <E> void set(Generic<T> a) {
}
}
三、限定类型变量 extends
extends可以用在类、接口、方法中。
public static <T> T min(T a, T b) {
if (a.compareTo(b) > 0) {
return a;
} else {
return b;
}
}
上边的代码是有问题的,因为T类型不一定有compareTo()方法。所以我们如果想实现代码逻辑,可以使用extends来做限定。
public class ExtendsTest {
/**
* 使用extends来做限定,这样传过来的参数必须是实现了Comparable接口的参数
*/
private <T extends Comparable> T min(T a, T b) {
if (a.compareTo(b) > 0) {
return b;
} else {
return a;
}
}
public static void main(String[] args) {
ExtendsTest test = new ExtendsTest();
//这样写会报错,因为Test()类没有实现Comparable参数
test.min(new Test(), new Test());
//这样使用是没有问题的
test.min(new Test2(), new Test2());
}
}
class Test {
}
class Test2 implements Comparable {
@Override
public int compareTo(Object o) {
return 0;
}
}
继承的可以是类,也可以是接口,可以继承多个接口,只能继承一个类,并且类必须放在最前边,用&隔开。
//1、可以定义多个泛型
//2、继承的可以是类,也可以是接口,可以继承多个接口,只能继承一个类,并且类必须放在最前边,用&隔开
public static <T extends ArrayList & Comparable & Serializable,K extends Comparable> T min(T a, K b) {
if (a.compareTo(b) > 0) {
return a;
} else {
return a;
}
}
四、泛型中的约束和局限性
public class Restrict<T> {
private T data;
public Restrict() {
}
public Restrict(T data) {
this.data = data;
//1、不能实例化类型变量
//T t=new T();
}
//2、静态域或方法里不能引用类型变量
//(因为在对象创建的时候,才知道泛型的类型,虚拟机创建一个对象的时候,先执行static代码,然后才执行构造方法)
private static T instance;
private static void fun(T t) {
}
//3、静态方法本身是泛型方法的情况是可以引用类型变量的
private static <T> T fun2(T t) {
T t1 = t;
return t1;
}
public static void main(String[] args) {
//4、基础类型不允许作为实例化的具体参数,必须用包装类型
Restrict<double> restrict1=new Restrict<double>();//这样是错误的
Restrict<Double> restrict2 = new Restrict<>();
//5、泛型中不支持使用instanceof
if(restrict2 instanceof Restrict<Double>)
if(restrict2 instanceof Restrict<T>)
//6、结果是true,不管传入的是什么类型的参数,getClass()获取的是泛型类的原生类型
Restrict<String> restrictString = new Restrict<>();
System.out.println(restrictString.getClass() == restrict2.getClass());
//7、可以定义泛型数组,但是不能创建泛型数组
Restrict<Double>[] restrictArray;
Restrict<Double>[] restrictArray2=new Restrict<Double>[];
}
}
public class ExceptionRestrict {
//8、泛型类不能 extends Exception/Throwable
private class Problem<T> extends Exception{
}
//9、泛型中不能捕获泛型类对象
public <T extends Throwable> void doWork(T t){
try{
}catch (T t){
}
}
//10、但是可以抛出泛型类对象
public <T extends Throwable> void doWork(T t) throws T{
try{
}catch (Throwable e){
throw t;
}
}
}
五、泛型类型的继承规则
类型之间的继承关系不能代表泛型进行参数实例化时候的继承关系。
public class ClassOne<T> {
public static void main(String[] args) {
//ClassOne<Fruit>和ClassOne<Apple>之间没有任何继承关系
ClassOne<Fruit> fruit=new ClassOne<>();
ClassOne<Apple> apple=new ClassOne<>();
//这样会报错,因为他们两个之间没有继承关系
//ClassOne<Fruit> fruit2=new ClassOne<Apple>();
}
}
class Fruit{ }
class Apple extends Fruit{ }
泛型类可以继承其他泛型类
public class ClassOne<T> {
private static void setData(ClassOne<Apple> a){}
public static void main(String[] args) {
ClassOne<Fruit> class1=new ClassOne<Fruit>();
//创建泛型类的子类对象
ClassOne<Apple> class2=new ExtendsClassOne<Apple>();
setData(class2);
//这样传值有问题,setData()方法接受的泛型类型是Apple
//setData(class1);
}
}
class ExtendsClassOne<T> extends ClassOne<T>{}
class Fruit{ }
class Apple extends Fruit{ }
六、通配符?
前边说到,类型之间的继承关系不能代表泛型进行参数实例化时候的继承关系。但是有时候我们希望类型之间的继承关系应用到泛型中,这个时候就用到通配符?了。
public class Food {
}
public class Fruit extends Food{
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Apple extends Fruit {
}
public class Orange extends Fruit {
}
public class HongFuShi extends Apple {
}
public class GenericType<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
1、? extends
public class WildChar {
/**
* 1、表示传进来的类型参数是Fruit的子类,包括Fruit本身,传入类型的上界是Fruit
* 传进来的参数不能超过Fruit(Apple、Orange、HongFuShi可以,Food不可以)
* 2、extends主要用于安全的访问数据
*/
public static void printExtends(GenericType<? extends Fruit> p) {
System.out.println(p.getData().getColor());
}
public static void use2() {
GenericType<Fruit> a = new GenericType<>();
printExtends(a);
GenericType<Orange> b = new GenericType<>();
printExtends(b);
GenericType<HongFuShi> f = new GenericType<>();
printExtends(f);
GenericType<Food> c = new GenericType<>();
//这样编写错误,传进来的参数不能超过Fruit
printExtends(c);
//可以这样赋值
GenericType<? extends Fruit> d = b;
GenericType<? extends Fruit> e = f;
//get的一定是Fruit,Fruit一定能安全转型为它的超类
GenericType<? extends Fruit> genericType=new GenericType<>();
Fruit fruit2 = genericType.getData();
Food food=genericType.getData();
/**
* 这样编写错误,因为extends主要用于安全的访问数据,
* genericType.getData()只能确保得到的是Fruit类型数据
* Fruit类型不能安全转型为它的子类。
*/
Apple apple2 = genericType.getData();
Apple apple = new Apple();
Fruit fruit = new Fruit();
//下边这两行编写错误,因为带通配符 ? extends定义的变量,只能安全的访问数据,不能修改数据
genericType.setData(apple);
genericType.setData(fruit);
}
}
2、?super
public class WildChar {
/**
* 1、super 表示传递给方法的参数必须是Apple的超类,
* 包括Apple本身(如:Fruit、Food可以,HongFuShi、Orange不可以),传入的下界是Apple
* 2、super主要用于安全的写入数据
*/
public static void printSuper(GenericType<? super Apple> p) {
System.out.println(p.getData());
}
public static void useSuper() {
GenericType<Fruit> fruitType = new GenericType<>();
GenericType<Apple> appleType = new GenericType<>();
GenericType<HongFuShi> hongFuShiType = new GenericType<>();
GenericType<Orange> orangeType = new GenericType<>();
printSuper(fruitType);
printSuper(appleType);
//下边两行编码错误
printSuper(hongFuShiType);
printSuper(orangeType);
GenericType<? super Apple> genericType = new GenericType<>();
//set的一定是Apple,Apple的超类不能安全的转型为Apple,但Apple的子类一定能安全的转型
genericType.setData(new Apple());
genericType.setData(new HongFuShi());
genericType.setData(new Fruit());
//返回类型只能是Object(因为返回的是Apple的超类,但不知道是哪个超类,所以返回所有超类的超类)
Object data = genericType.getData();
}
}
3、通配符只能用在方法中。
//这样定义有问题,通配符不能用在类中
public class ClassOne<? extends Fruit>{}