java核心基础 --- 泛型

本篇博文主要介绍两部分,为何要有泛型以及泛型擦除这个概念,若是你想要了解泛型的具体使用,请查看相关书籍或者其余博客。java

为何要有泛型

来看看官方文档给的解释:安全

Code that uses generics has many benefits over non-generic code:app

  • Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.ui

  • Elimination of casts.this

    The following code snippet without generics requires casting:code

    List list = new ArrayList();
    list.add("hello");
    String s = (String) list.get(0);

    When re-written to use generics, the code does not require casting:blog

    List<String> list = new ArrayList<String>();
    list.add("hello");
    String s = list.get(0);   // no cast
  • Enabling programmers to implement generic algorithms. By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.ip

文档中主要列举了三个使用泛型的理由。文档

  1. 首先,若是代码中有使用泛型的话,编译器就会进行类型检查,当代码中违反了类型安全的规范,那么编译就不会经过。在编译期间修改错误相对来讲比在运行期间修改错误容易得多。get

  2. 其次,泛型能够消除类型转换的错误。最多见的一个例子是在集合类的使用中,在没有使用泛型以前,集合类中能够添加任何的类型,当咱们须要获取容器中的元素时,就要进行类型转换:

    List list = new ArrayList();
    list.add("hello");
    String s = (String)list.get(0);

    通常状况下,这种作法是没问题的。可是,当咱们添加的元素既有 String 又有其余类型时,上面的代码在运行期间就很容易报错。使用泛型以后,咱们就只能向容器中添加单一的元素,这样就能够很好地解决该问题,除此以外,获取元素时也能够不用进行类型的转换了,十分方便。

  3. 最后,咱们能够使用泛型方法来提升方法的重用性。当咱们有两个方法,一个方法返回 String,另外一个方法返回 int,若是不使用泛型的话,咱们须要写两个方法:

    public String getString(String message){
        return message;
    }
    public int getInt(int message){
        return message;
    }

    咱们能够看到,上面两个方法体实际上是同样的,分红两个方法写不免有点冗余,这时泛型方法就能够派上用场了。使用泛型方法,不只使代码的冗余度下降了,对于返回类型咱们也不用进行类型转换,一箭双雕,何乐而不为呢?

    public <T> T getMessage(T message) {
        return message;
    }

泛型擦除

了解了为何要使用泛型后,咱们来看看泛型中另外一个重要的概念 --- 泛型擦除。首先咱们来看一下这个例子:

public class ErasedTRypeEquivalence {
	public static void main(String[] args) {
		Class c1 = new ArrayList<String>().getClass();
		Class c2 = new ArrayList<Integer>().getClass();
		System.out.println(c1 == c2);
	}
}

Class 类包含的是类信息,直观的来看,ArrayList<String> 和 ArrayList<Integer> 二者包含的类信息应该是不同的,可是运行的结果倒是 true。这是由于泛型只在编译期间才起做用,在运行期间,泛型信息都会被擦除成「原生」类型,上面的 ArrayList<String> 和 ArrayList<Integer> 会被擦除为 ArrayList<Object>。举个例子,当咱们使用泛型时,在调用 ArrayList.add() 方法时,编译器会提醒咱们须要添加的元素为 Integer 类型

add

可是在运行期间,其 add 方法能够添加的元素仍然是 Object 类型。怎么验证这一点呢?看下面这段代码:

public class EraseTest {
	public static void main(String[] args) {
		Apple<Integer> a = new Apple<>(6);
		Field[] fs = a.getClass().getDeclaredFields();
		for(Field f : fs) {
			System.out.println(f.getType().getName());
		}
	}
}

class Apple<T>{
	T size;
	public Apple(T size) {
		this.size = size;
	}
}

咱们为 Apple 指定的泛型是 Integer,按照常理,size 的类型也应该是 Integer,可是上面的代码却打印出 java.lang.Object,因而可知,泛型只是存在于编译期间,在运行期间其类型会被擦除。有了泛型擦除这个概念,咱们就能够更好的理解使用泛型过程当中编译器的警告以及错误提示,对于泛型的一些特性也可以更好的理解。

这篇文章就到这边了,若是其中有什么问题的话欢迎评论留言!

相关文章
相关标签/搜索