Java中的泛型

一、泛型的好处

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>{}

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值