第六章 java重要知识点(泛型和通配符)

泛型

JDK 1.5引入泛型,将代码适用于多种数据类型
如果用Object来实现存储不同的数据类型,虽然可以存储进去,但是拿出来的话,不知道数据下标存放的元素类型,会出现报错。

用法

class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分类型参数
}

规则:一般用以下大写字母来表示形参
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
注意:
在Java中不能new泛型类型的数据
例如:T[] t = new T[5];
可以对New出来的数据机型强转:
T[] array = (T[])new Object[10];

语法

泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象
泛型类的尖括号中需要用包装类,不能用基本类型,第二个尖括号中的内容可以省略

MyArray<Integer> list = new MyArray<Integer>();

裸类型

泛型类,但是没用带类型,是为了兼容老版本遗留的语法规则
如下:

MyArray list = new MyArray();

泛型的编译

擦除机制

在字节码文件中,并没有T这类的符号出现,而都是Object,这种编译方式称之为擦除机制

不能实例化泛型数组

泛型是否有问题是在编译进行检测,但数组是在运行时,检测类型的匹配问题。
应该用反射的方法来,实例化泛型数组。

泛型的上界

语法

class 泛型类名称<类型形参 extends 类型边界> {}

用extends关键字来定义泛型的上界,这样的话用户在传泛型参数的时候只能传上界及其子类

public class MyArray<E extends Number> {
...
}

当我们的类型边界写的是一个接口时,那么用户传的参数必须是实现了该接口的

public class MyArray<E extends Comparable<E>> {
...
}

则E必须实现Comparable接口

泛型方法

语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { … }

public class Util {
	public static <E> void swap(E[] array, int i, int j) {
		E t = array[i];
		array[i] = array[j];
		array[j] = t;
	}
}

在使用这个方法时,我们可以直接传类型参数,也可以不传,让编译器进行类型推导

Integer[] a = { ... };
swap(a, 1, 2);

Integer[] a = { ... };
Util.<Integer>swap(a, 1, 2);

通配符

定义

泛型无法解决协变问题。具体是:泛型之间没用父子类的关系,泛型中传什么参数就说什么参数。通配符的作用就是可以实现父子类的关系,使得参数范围更广

通配符的上界

<? extends 上界>
<? extends Number>//可以传入的实参类型是Number或者Number的子类

通配符确定上界一般用来读取数据,而不是写入数据因为不确定传入的类型是对应的哪个子类,所以无法进行set,一般都是get进行读取

class Apple extends Fruits{
    
}
class Banana extends Fruits{
    
}
class Fruits extends Meat{
    
}
class Meat{
    
}
class  Food<T>{
    private T name;
    public T getName(){
        return this.name;
    }
    public void setName(T name){
        this.name = name;
    }
}

public class Demo {

    public static void main(String[] args) {
        Food<Apple> appleFood = new Food<>();
        //因为Food<T> 给的类型是Apple,所以你只能给相同类型的数据
        appleFood.setName(new Apple());
        fun(appleFood);
        
        Food<Banana> bananaFood = new Food<>();
        bananaFood.setName(new Banana());
        fun(bananaFood);
    }
    public static void fun(Food<? extends Fruits> tmp){//1.
        System.out.println(tmp.getName());
        tmp.setName(new Banana());//2.无法修改
        tmp.setName(new Apple());//3.无法修改
        Fruits ret = tmp.getName();//读取数据

    }

注释1处:使用了上界通配符,表示可以传入的实参类型是Fruits和Fruits的子类型。
注释2、3处:由于是不确定类型的,所以无法修改,因为tmp接收的是Fruits和它的子类,此时存储的元素类型应该是哪个子类,是无法确定的,所以设置会报错!但是可以获取元素,因为ret的类型的Fruits,是所有能传入参数的父类。
通配符上界:可以读取数据,不能写入数据

通配符的下界

用super来描述通配符的下界

<? super 下界>
<? super Integer>//可以传入的实参的类型是Integer或者Integer的父类类型
public static void main(String[] args) {
        Food<Fruits> f1 = new Food<>();
        f1.setName(new Fruits());
        fun(f1);
        
        Food<Meat> f2 = new Food<>();
        f2.setName(new Meat());
        fun(f2);
    }
public static void fun(Food<? super Fruits> tmp){//1.
        System.out.println(tmp.getName());//只能直接输出
        tmp.setName(new Banana());//2.可以修改
        tmp.setName(new Fruits());//3.可以修改
        Fruits ret = tmp.getName();//4.不能读取数据

    }

注释1处:使用的是通配符下界,表示接受的参数只能是Fruits或者是Fruits的父类。
注释2、3处:可以修改,因为tmp的类型是Fruits或者它的父类,只能设置Fruits的子类或者它本身。
注释4处:如果此时传入的参数是Fruits的父类,用Fruits类型来接收是不安全的,换句话说,传入的参数是不确定是哪个父类,不能接收。
通配符下界:不能读取数据,可以写入数据。

总结边界

  1. <?>无边界通配符

可以传入任意实参类型,不能写入,不能读取,只能输出。
2. <? extends 上界>
可以传入实参类型是上界或者上界的子类,不能写入,可以读取。
3. <? super 下界>
可以传入实参类型是下界或者下界的父类,不能读取,可以写入数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值