一次解决Java泛型问题

JAVA泛型的价值与本质:为了实现代码更好的复用。

JDK1.5时期,通常使用Object作为通用参数类型来实现复用,但是出现个非常危险的运行时类型问题,泛型是实现了类型的参数化,解决了JDK1.5时期类型转换的不安全问题(ClassCastException)。泛型实现编译时检查而不是运行时抛出异常。

JAVA的泛型是伪泛型,实现上Java泛型通过类型擦除实现:

java泛型擦除的初衷是希望让泛型化程序的调用方可以依赖非泛型的库,反之亦然。(移植性兼容)类型擦除实现了移植兼容性,但是从本质初衷触发的“安全代码复用”,也带来也一些能力限制和安全问题:如支持原生态类型,不支持实例化泛型数组。

普通泛型参数:Result<T> 直接擦除为 Result<Object>

有限的通配符:Result<T extens Number> 擦除为 Result<Number>

理解这个实现就能理解很多Java泛型使用局限的原因。

Effective 中关于 泛型的最佳实践:

最佳实践1:禁止使用原生态类型

List<E> 对应的原生态类型是List,在java中是可以直接使用List的,但是会导致编译时的异常。

最佳实践2:列表优于数组

数组是协变的儿泛型是可变的。

(协变:如果A是B的子类型,那么数组A[]是数组B[]的子类型)

对于任意两个类型A,B,List<A> 既不是list<B>的子类也不是它的父类。

数组是具体化的,泛型是类型擦除的。

数组在运行时,知道和强化他们的元素类型。泛型只在编译时IDE强化它的类型,但是在运行时丢弃它的类型。

最佳实践3:谨慎混用泛型和可变参数

直接实例化泛型数组是非法的:

List<String> [] stringLists =  new List<String>[1];// 直接实例化泛型数组,预发报错

可变参数中使用泛型数组却是合法的:

void doSomething(List<String> ... stringLists) //最为可变参数是合法的。

为什么会出现上面的矛盾?带有泛型的可变参数或者参数或类型的方法在实践中用处很大,Java的设计者选择容忍这一矛盾的存在。

如Java类库中的Arrays.asList(T ... a) ;Collections.addAll(Collection(? super T) c, T ... elements) ; EnumeSet.of(E first, E ... rest)

如何规避线上报错?

不能修改可变参数数组中的任何值,可变数据只用来动作消费者使用,而不是生产者。

不降可变参数数组赋值给任何父类数组。

最佳实践4:利用有限制通配符提升API的灵活性

为了最大提升API的灵活性,要在表示生产者和消费者的输入参数上使用有限制通配符。

上界:<? extends Super>   下界:<? super Sub>

 以上代码的编译并不能通过,原因是Java编译器拒绝向指定下界通配符定义的集合里进行add操作。

PECS,producer extends consumer super:

使用extends关键子定义的泛型集合只可以不可以写。

使用super可以用来写,不可以用来读。

static class Fruit{
        @Override
        public String toString() {
            return super.toString();
        }
    }
    static class Apple extends Fruit{
        @Override
        public String toString() {
            return super.toString();
        }
    }
    static class Orange extends Fruit{
        @Override
        public String toString() {
            return super.toString();
        }
    }

    // 泛型接口 本质上是定义了个规约模板
    static interface FruitPlate<T> {
        public T getFruit();
    }

    static class FruitDish<T> implements FruitPlate<T> {
        List<T> fruit;
        @Override
        public T getFruit() {
            return fruit.get(0);
        }
    }

    // 泛型类
    static class FruitHolder<T extends Fruit>{
        T fruit;
        // 泛型方法
        FruitHolder(T fruit) {
            this.fruit = fruit;
        }
        public String name() {
            return fruit.toString();
        }
    }

    public static Fruit get(List<? extends Fruit> fruits) {
        // 编译不通过
        //fruits.add(new Orange());
        Fruit fruit = fruits.get(0);
        System.out.println(fruit);
        return fruit;
    }

    // ?通配符 当赋值的类型不确定的时候,我们用通配符(?)代替了
    public static void set(Collection<? super Apple> apples, Apple apple1, Apple apple2) {
        apples.add(apple1);
        apples.add(apple2);

        // 编译不通过 编译器不能确定 apples里的类型
        //Apple apple = apples.get(0);
    }


    public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements) {
            result |= c.add(element);
        }
        return result;
    }

以上,如有不清晰欢迎留言提问。

如有收获,欢迎关注,点赞,转发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值