泛型学习笔记

允许在定义类、接口、方法时使用类型形参,当使用时 指定具体类型。
集合体系中的所有类都增加了泛型,泛型也主要用在集合。
泛型的上限和下限

上界<? extends T>不能往里存 ,只能往外取。
下界<? super T>不影响往里存 ,但往外取只能放在Object对象里。

泛型类

public class ClassGenericity {
    public static void main(String[] args) {
        /** 创建ObjectTool对象并指定元素类型为String */
        ObjectTool<String> stringTool = new ObjectTool<>();
        stringTool.setObj("muse");
        System.out.println(stringTool.getObj());

        /** 创建ObjectTool对象并指定元素类型为Integer */
        ObjectTool<Integer> integerTool = new ObjectTool<>();
        // integerTool.setObj("muse"); // 编译报错
        integerTool.setObj(10);
        System.out.println(integerTool.getObj());
    }
    /**
     * 构建可以存储任何类型对象的工具类
     */
    static class ObjectTool<T> {
        private T obj;
        public T getObj() {
            return obj;
        }
        public void setObj(T obj) {
            this.obj = obj;
        }
    }
}

泛型方法

public class MethodGenericity<T> {
    public static void main(String[] args) {
        //创建对象
        ObjectTool tool = new ObjectTool();

        /** 调用方法,传入的参数是什么类型,T就是什么类型 */
        tool.show("hello");
        tool.show(12);
        tool.show(12.5f);
    }

    static class ObjectTool {
        //定义泛型方法
        public <T> void show(T t) {
            System.out.println(t);
        }
    }
}

泛型类派生出的子类

public class SubclassGenericity {
    public static void main(String[] args) {
        // 测试第一种情况
        Inter<String> i = new InterImpl1();
        i.show("hello");

        // 编译错误
//        Inter<Integer> ii = new InterImpl1();
//        ii.show(1);

        // 第二种情况测试
        Inter<String> iii = new InterImpl2();
        iii.show("100");
    }
}

/**
 * 把泛型定义在接口上
 */
interface Inter<T> {
    void show(T t);
}

/**
 * 实现一:子类明确泛型类的类型参数变量
 */
class InterImpl1 implements Inter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

/**
 * 实现二:子类不明确泛型类的类型参数变量,实现类也要定义出T的类型
 */
class InterImpl2<T> implements Inter<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

泛型的上限和下限
PECS原则

// https://www.cnblogs.com/huoqm/p/13931363.html
// https://www.cnblogs.com/zhaoyibing/p/9051428.html
public class Pecs {

    public static void main(String[] args) {
        testPECSextends();
        testPECSsuper();
    }

    /**
     * 【读取】
     * 如果要从集合中【可读取】类型T的数据,并且【不能写入】,可以使用 ? extends 通配符;(Producer Extends)
     * List<? extends Animal> animals 里面能够存放什么呢?
     * 动物、狗、猫、猪、鸡... 只要是动物,都有可能被存入进animals里。
     */
    public static void testPECSextends() {
        List<Dog> dogs = Lists.newArrayList(new Dog());
        List<? extends Animal> animals = dogs;

        /**
         * animals是一个Animal的子类的List,由于Dog是Animal的子类,因此将dogs赋给animals是合法的,但是编译器会阻止将new Cat()加入animals。
         * 因为编译器只知道animals是Animal的某个子类的List,但并不知道究竟是哪个子类,为了类型安全,只好阻止向其中加入任何子类。那么可不可以加入
         * new Animal()呢?很遗憾,也不可以。事实上,不能够往一个使用了? extends的数据结构里写入任何的值。
         */
        // animals.add(new Cat()); // 编译失败
        // animals.add(new Animal()); // 编译失败
        // animals.add(new Dog()); // 编译失败

        /**
         * 由于编译器知道它总是Animal的子类型,但并不知道具体是哪个子类。因此我们总可以从中读取出Animal对象:
         */
        Animal animal = animals.get(0);
        Object obj = animals.get(0);
        // Dog dog = animals.get(0); // 编译失败
    }

    /**
     * 【写入】
     * 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
     * <p>
     * 如果既要存又要取,那么就不要使用任何通配符。
     *
     */
    public static void testPECSsuper() {
        List<Animal> animals = Lists.newArrayList();
        List<? super Dog> dogs = animals;
        /**
         * 这里的animals是一个Animal的超类(父类,superclass)的List。同样地,出于对类型安全的考虑,我们可以加入Dog对象或者其任何子类(如WhiteDog)对象,
         * 但由于编译器并不知道List的内容究竟是Dog的哪个超类,因此不允许加入特定的任何超类型。
         */
        dogs.add(new Dog());
        dogs.add(new WhiteDog());
        // dogs.add(new Animal()); // 编译失败
        // dogs.add(new Cat()); // 编译失败
        // dogs.add(new Object()); // 编译失败

        /**
         * 而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。
         */
        Object obj = dogs.get(0);
        // Dog dog = dogs.get(0); // 编译失败
        // Animal animal = dogs.get(0); // 编译失败
    }
}

类型通配符

public class TypeWildcard {

    private final static List<Integer> INTEGER_LIST = Lists.newArrayList(1, 2, 3, 4);

    private final static List<Object> OBJECT_LIST = Lists.newArrayList("a", 1, 'c', 6.0F, 100L, true);

    private final static List<String> STRING_LIST = Lists.newArrayList("x", "y", "z");

    public static void main(String[] args) {
        TypeWildcard typeWildcard = new TypeWildcard();
        typeWildcard.test1(INTEGER_LIST);
        System.out.println("------------test1 end-------------");

        // typeWildcard.test2(STRING_LIST);
        typeWildcard.test2(OBJECT_LIST);
        System.out.println("------------test2 end-------------");

        typeWildcard.test3(INTEGER_LIST);
        typeWildcard.test3(OBJECT_LIST);
        typeWildcard.test3(STRING_LIST);
        System.out.println("------------test3 end-------------");

        typeWildcard.test4(INTEGER_LIST);
        typeWildcard.test4(OBJECT_LIST);
        typeWildcard.test4(STRING_LIST);
        System.out.println("------------test4 end-------------");

        /** 上限通配符 */
        typeWildcard.test5(INTEGER_LIST);
        System.out.println("------------test5 end-------------");

        /** 下限通配符 */
        // typeWildcard.test6(INTEGER_LIST); 编译错误
        System.out.println("------------test6 end-------------");

    }

    public void test1(List list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }

    public void test2(List<Object> list) {
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }

    public <T> void test3(List<T> list) {
        list.add(list.get(0));
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }

    public void test4(List<?> list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }

    public void test5(List<? extends Number> list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }

    public void test6(List<? super Number> list) {
        // list.add(list.get(0)); // 编译错误
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
    }
}

类型擦除与桥接方法

由于类型擦除与桥接方法 泛型是提供给javac编译器使用的,它用于限定集合的输入类型, 让编译器在源代码级别上,即挡住向集合中插入非法数据。但编 译器编译完带有泛形的java程序后,生成的class文件中将不再带 有泛型信息,以此使程序运行效率不受到影响,这个过程称之为 “擦除”。 由于类型被擦除了,为了维持多态性,所以编译器就自动生成了 桥接方法。

被擦出后的泛型类

// public class Node<T> {
//
//     public T data;
//
//     public void setData(T data) {
//         this.data = data;
//     }
// }
//
// class MyNode extends Node<Integer> {
//
//     public Integer data;
//
//     public void setData(Integer data) {
//         this.data = data;
//     }
// }

/**
 * 被擦出后的泛型类
 */
public class Node {
    public Object data;

    public void setData(Object data) {
        this.data = data;
    }
}

/**
 * 桥接方法
 */
class MyNode extends Node {
    // 然后内部调用桥接方法,用于对入参类型的限制
    public void setData(Integer data) {
        super.setData(data);
    }

    // 首先请求该方法
    @Override
    public void setData(Object data) {
        setData((Integer) data);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值