写点什么

Java 基础总结三(泛型、异常,阿里 java 面试题及答案

用户头像
极客good
关注
发布于: 刚刚

private Object[] instances = new Object[0];


public T get(int index) {


return (T) instances[index];


}


public void set(int index,T newInstance) {


instances[index] = newInstance;


}


public void add(T newInstance) {


instances = Arrays.copyOf(instances,instances.length + 1);


instances[instances.length - 1] = newInstance;


}


}


看一下使用方法:


TestList<String> testList = new TestList<>();


testList.add("wlk");


String str = testList.get(0);


System.out.println(str); // wlk

泛型接口

我们除了创建泛型类,泛型接口与其很类似:


public interface Shop <T>{


T buy();


float refund(T item);


}


// 实现


public class RealShop<E> implements Shop<E>{


@Override


public E buy() {


return null;


}


@Override


public float refund(E item) {


return 0;


}


}


再比如,我们想要实现一个水果商店:


public class Fruit {


}


public class Apple extends Fruit{


}


public class FruitShop<E> implements Shop<E>{


@Override


public E buy() {


return null;


}


@Override


public float refund(E item) {


return 0;


}


}


FruitShop<Apple> fruitShop = new FruitShop<>(); // 这个商店卖苹果


FruitShop<Phone> stringShop = new FruitShop<>(); // 这个水果商店卖手机??? 当然不和逻辑


由于我们的商店泛型 E 没有加以限制,会导致错误,我们来给他加上限制:


public class FruitShop<E extends Fruit> implements Shop<E>{


@Override


public E buy() {


return null;


}


@Override


public float refund(E item) {


return 0;


}


}


现在这个水果商店只可以卖 Fruit 或者 Fruit 的子类,如 Apple,而不能卖手机。

泛型方法

自己声明了泛型的方法


泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。


我们给我们的苹果商店创建一个方法,它支持用其他水果换取苹果:


public class FruitShop<E extends Fruit> implements Shop<E>{


@Over


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


ride


public E buy() {


return null;


}


@Override


public float refund(E item) {


return 0;


}


public <T extends Fruit> E exchange(T item){


return null;


}


}


// 使用


FruitShop<Apple> appleShop = new FruitShop<>();


Apple apple = appleShop.<Banana>exchange(new Banana());


//类型推导,<Banana>可省略


Apple apple = appleShop.exchange(new Banana());

类型擦除

泛型的好处是在编译期进行类型检查和类型转换,且泛型只在编译期有效。泛型是在 JDK 1.5 里引入的,如果不做泛型擦除,那么 JVM 需要对应使得能正确的的读取和校验泛型信息;另外为了兼容老程序,需为原本不支持泛型的 API 平行添加一套泛型 API。


在运行期,声明时的泛型参数会被擦除,在使用处的泛型会被泛型的父类替换,如<E extends Fruit>,此时使用E的地方会被 Fruit 替换。

泛型的协变与逆变

定义:如果 A、B 表示类型,f()表示一个类型的构造函数,Type1≤Type2表示 Type1 是 Type2 的子类型,Type1≥Type2表示 Type1 是 Type2 的超类型;


  • f()是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;

  • f()是逆变(contravariant)的:当A≤B时有f(B)≤f(A)成立;

  • f()是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)f(B)相互之间没有继承关系。


对于任意两个不同的类型 Type1 和 Type2,不论它们之间具有什么关系,给定泛型类 G<T>G<Type1>G<Type2> 都是没有关系的,即 Java 中泛型是不变的,而数组是协变的,可以验证以下:


ArrayList<Number> list = new ArrayList<Integer>(); // 编译器报错


// 可以顺利通过编译,正常使用。


Number[] arr = new Integer[5];


arr[0] = 1;


// 但也有一定的问题,这个数组接受 Number 其他子类也不会在编译期报错,而在运行期报错。泛型从根源杜绝了这种错误


在 Java 的泛型中,是可以支持协变与逆变的,但会有很大的限制:


  • ? extends T(上边界通配符)实现协变关系,表示?是继承自T的任意子类型。也表示一种约束关系,只能提供数据,不能接收数据。

  • ? super T(下边界通配符)实现逆变关系,表示?T的任意父类型。也表示一种约束关系,只能接收数据,不能提供数据。


协变所谓只能提供数据,不能接收数据,是指在实例化出泛型类后,仅可以调用该类中返回值为泛型的方法,而不可以调用参数为该泛型的方法。


逆变则恰好相反。


那么如此限制,?在我们实际开发中应该如何应用呢?


List<? extends Number> list = new ArrayList<Integer>();


这样的用法在我们平时是用不到的,只有一些场景化的需要,才会使用到泛型通配符。就拿我们的水果商店举例,我们现在有这样一个需求,需要计算集合中水果的总重量,所以编写了如下方法:


public int totalWeight(List<? extends Fruit> list) {


int total = 0;


for(Fruit fruit : list) {


total += fruit.weight;


}


return total;


}


// 使用起来,就可以向这个方法中传入任意 Fruit 子类的集合了


List<Apple> appleList = new ArrayList<>();


totalWeight(appleList);


List<Banana> bananaList = new ArrayList<>();


totalWeight(bananaList);


再举一个逆变的用法:


List<Apple> appleList = new ArrayList<Fruit>();


当然这种用法是极其少有的,不做讨论。我们给苹果类添加一个方法,这个方法的作用就是给苹果装入集合:


public class Apple extends Fruit{

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Java基础总结三(泛型、异常,阿里java面试题及答案