Java泛型

一.为什么会使用泛型

从Java程序设计语音1.0以来,变化最大部分就是泛型.泛型正是我们需要的程序设计手段.使用泛型机制编写的程序代码要比哪些杂乱的使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性.

在Java中增加泛型类之前,泛型程序设计就是用继承实现的.ArrayList类只维护一个Object引用的数组.类似于下面的代码:

public class Arraylist{
		private Object [] elementData;
		...
		public Object get(int i){...}
		public void add(Object o){...}
	}

这样实现会有两个问题:①单获取一个值时必须进行强制类型转换.②可以向数组列表中添加任何类的对象,编译和运行都不会出错,然而在其他地方,将get的结果强制转换成其它类型,程序就会报错.

泛型提供了一个更好的解决方案,参数类型(Type Pararmeters).ArrayList类有一个类型参数用来指示元素的类型:

ArrayList<String> arrayList = new ArrayList<String>();

这使得代码有更好的可读性,人们一看就知道这个数组列表中包含的是String对象.
 

二.泛型类

首先定义一个简单的sutdent类

public class Student {
	
	private String major;
	
	public Student(String major) {
		this.major = major;
	}

	public String getMajor() {
		return major;
	}

	public void setMajor(String major) {
		this.major = major;
	}

}

这样做的一个坏处是Student里面现在只能装入String类型的元素,今后如果我们需要装入Integer等其他类型的元素,还必须要另外重写一个Student,代码得不到复用,使用泛型可以很好的解决这个问题。

public class Student<T> {
	
	private T major;

	public T getMajor() {
		return major;
	}

	public void setMajor(T major) {
		this.major = major;
	}
}

这样我们的Student就可以复用,使用时只要将T换成想要的类型.

三.泛型方法

声明一个泛型方法只要在返回类型前面加上一个类型变量<T>就可以了:

public class Test {
    public static <T> boolean compare(T a,T b) {
        return a > b;
    }
}
public class Pair<T> {
    private T value;
    public Pair(T value) {
        this.value = value;
    }
    public void setValue(T value) { this.value = value; }
    public T getValue() { return value; }
}

对泛型方法的调用如下:

Pair<Integer> a = new Pair<>(1);
Pair<Integer> b = new Pair<>(2);
boolean same = Test.compare(p1, p2);

在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。那么对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?

public class GenericTest {

    public static void main(String[] args) {

        Pair<Number> a = new Pair<>(1);
        Pair<Integer> b = new Pair<>(2);

        System.out.println("a class:" + a.getClass());      // com.test.Pair
        System.out.println("b class:" +b.getClass());        // com.test.Pair
        System.out.println(a.getClass() == b.getClass());    // true

    }

}

由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Pair),当然,在逻辑上我们可以理解成多个不同的泛型类型。

究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

四.通配符

对于上面第三点代码中的泛型方法,现在做如下调用:

Pair<Number> a = new Pair<>(1);
Pair<Integer> b = new Pair<>(2);
getData(a);
getDate(b);

public static void getData(Pair<Number> data) {
         System.out.println("data :" + data.getData());
}

上面的代码在运行getData方法的时候会报错,原因是因为在泛型中Pair<Number> a 和Pair<Integer>,是不能等同,也不能视为子父类关系的.对此我们可以这样思考,如果把它们视为子父类,那么通过getData()方法取出的数据是什么类型呢?

那么这种情况应该怎么解决呢?通配符 <?> 就应运而生了,注意了,此处是类型实参,而不是类型形参!且Box<?>在逻辑上是Pair<Integer>、Pair<Number>...等所有Box<具体类型实参>的父类。

Pair<Number> a = new Pair<>(1);
Pair<Integer> b = new Pair<>(2);
getData(a);
getDate(b);

public static void getData(Pair<?> data) {
         System.out.println("data :" + data.getData());
}

上面使用通配符这段代码就不会有问题了.

五.类型变量的限定

在通配符的代码上我们做如下修改:

Pair<Number> a = new Pair<>(1);
Pair<Integer> b = new Pair<>(2);
Pair<String> c = new Pair<>(2);
getData(a);
getDate(b);
getDate(c); //1

public static void getData(Pair<?> extends Number data) {
         System.out.println("data :" + data.getData());
}

上面的代码在 1 处会报错,原因是getData方法限定了泛型类型只能为Number类的子类,这就是类型通配符上限.用Pair<?> extends Number表示.

本文参考地址:https://www.cnblogs.com/lwbqqyumidi/p/3837629.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值