Java中的泛型-入门篇

原本今天是想接着写Java8新特性相关的文章的,在啃Stream的源码的时候,发现Stream对泛型的使用简直是牛逼!于是乎,想起我那可怜的Java基础。因此决定对泛型相关的知识点做一个全面的总结。写给自己看,讲给大家听。

什么是泛型

泛型(Generics)是JDK5中引入的一个新特性,泛型提供了编译时安全检查机制
泛型的本质是把“类型”也变成一种特殊的参数,即参数化类型。

泛型解决了什么痛点
public class IntegerSession {
private Integer start;
private Integer end;

public Integer getStart() {
return start;
}

public void setStart(Integer start) {
this.start = start;
}

public Integer getEnd() {
return end;
}

public void setEnd(Integer end) {
this.end = end;
}
}

public class BigDecimalSession {
private BigDecimal start;
private BigDecimal end;

public BigDecimal getStart() {
return start;
}

public void setStart(BigDecimal start) {
this.start = start;
}

public BigDecimal getEnd() {
return end;
}

public void setEnd(BigDecimal end) {
this.end = end;
}
}

观察上述的代码,IntegerSession,BigDecimalSession有很多共性,除了类名称和处理的类型不同之外,共通性是很高的!如果要对上述的两个类进行一种合并,我们会怎么编程呢!我们往往会通过基类来替代.比如用他们的顶层基类,Object,代码如下

public class ObjectSession {
private Object start;
private Object end;

public Object getStart() {
return start;
}

public void setStart(Object start) {
this.start = start;
}

public Object getEnd() {
return end;
}

public void setEnd(Object end) {
this.end = end;
}
}

我们设置区间为[1,2],然后来看看这三个类对取值的转换处理。如下图

显式转换问题.jpg

我们发现虽然引入了Integer/BigDecimal的基类Object想做一个兼容的处理,但是这会导致我们需要对取值做一些强制转换。虽然图片中的代码并不会出现错误,但是要注意到,Object是所有类的基类,如果我们设置的值是String类型的,那么在强制转换的时候就可能会出现类型转换错误,而这种错误只能到了运行期发现!这显然不合理。

类型转换问题.jpg

上面提到了一个痛点。程序在运行时可能发生的类型转换异常。接下来我们看看,泛型是如何解决这两个问题。我们引入一个泛型类,Session 。

public class Session<T> {
private T start;
private T end;

public T getStart() {
return start;
}

public void setStart(T start) {
this.start = start;
}

public T getEnd() {
return end;
}

public void setEnd(T end) {
this.end = end;
}
}

泛型解决强制转换问题.jpg

intSession指定的泛型参数 即限定了传入的参数类型必须是Integer类型。如果我们设置的数据是“一”,“二”,则显示如下。

泛型检查参数.jpg

从上面的例子中,我们可以得出两点,关于使用泛型的意义。
1.泛型能够讲类型参数化,使得代码的设计更加抽象,是一种模板设计的思想体现。
2.泛型提供一个及早发现类型安全问题的机制,即编译时的类型安全检查机制,使得代码更加安全。

常见的定义和使用
泛型类

泛型类的声明.jpg

泛型的声明方式非常简单,如果是声明泛型类,则在类名后加上<T,…省略>。
如果是声明泛型函数,那么则是在返回类型前加上<T,…省略>。凡是声明,一定要用尖括号包裹泛型参数。泛型变量的命名可以是任意的大写字母!但是往往都具有如下的一些规范

E:是Element的缩写,通常用于表示容器类中的存放元素,例如List ,Set
K,V:是Key/Value的缩写,通常用于表达键值对关系的泛型变量
N:是Number的缩写
T:Type,类型

泛型接口

泛型接口是具有泛型变量的interface,和泛型类的声明方式是一样的。在使用上需要注意,如果子类需要沿用泛型变量,则不应该在implement的时候讲泛型接口具体填充好,而是要和泛型接口保持一致的声明。

以我们常用的List 泛型接口为例,我们观察JDK自带的ArrayList 子类和我们自己实现一个List 子类,MyList的区别

public class MyList implements List<String> {
//...省略代码
}

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//...省略代码
}

public static void main(String[] args) {
MyList myList=new MyList();
List<String> arrayList=new ArrayList<String>();
}

通过上述的MyList和ArrayList的比较,我们不难发现我们自己实现的MyList子类,已经不具备泛型的特性了。这是因为在实现泛型接口的时候将泛型变量指定成具体类型String了。而观察ArrayList,它在实现泛型接口的同时,仍然延续了泛型接口变量 ,因此ArrayList是一个泛型类。

同样的,在extends关系中也有可能造成这个情况。如果我们希望子类在继承或者实现接口类的同时,保持泛型特性,不可把泛型具体化

泛型函数

泛型方法使得方法可以接收不同类型的参数。和泛型类的声明是一样的,泛型方法的声明也需要使用尖括号<T,…>的格式。例如我在ArrayList源码中翻阅到的toArray方法,就是一个泛型方法。

//ArrayList中的toArray方法就是一个泛型方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

在ArrayList中还有一个方法,是我们常用的get()方法,我们来看看它的源码

public E get(int index) {
rangeCheck(index);
return elementData(index);
}

get方法虽然返回的是泛型类型的数据,但是他并不是泛型方法,它只是ArrayList中的一个普通成员方法,它能够使用T作为返回参数,只不过是因为ArrayList在声明泛型类的时候已经声明过T变量了。

综上可见。泛型变量必须在类层面声明,或者在泛型函数中声明才能使用。泛型函数一定是具备<T,…>的。方法中使用泛型作为接收参数或者返回参数,不一定就是泛型方法!

学习总结

1.泛型(Generics)是JDK5中引入的一个新特性,泛型提供了编译时安全检查机制。泛型的本质是把“类型”也变成一种特殊的参数,即参数化类型。

2.合理使用泛型,能够使得代码更加健壮,更加精简

3.泛型变量的使用必须先经尖括号(<T,…>)的方式进行声明,可以在类层级(泛型类,泛型接口),或者方法层级(泛型方法)上进行声明。

4.使用了泛型变量作为入参或者出参的方法,不一定是泛型方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值