Java 编程思想(五)Java泛型

1. Java泛型的目的:

a. Java语言引入泛型的好处是安全简单。可以运行时错误提到编译时错误

java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的任意化任意化带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

b. 消除强制类型转换。 

消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。所有的强制转换都是自动和隐式的。

c. 提高性能。 

Lits list1 = new ArrayList();  
list1.add("CSDN_SEU_Cavin ");  
String str1 = (String)list1.get(0);  
List<String> list2 = new ArrayList<String>();  
list2.add("CSDN_SEU_Cavin ");  
String str2 = list2.get(0);  
对于上面的两段程序,由于泛型所有工作都在编译器中完成,javac编译出来的字节码是一样的(只是更能确保类型安全),那么何谈性能提升呢?是因为在泛型的实现中,编译器将强制类型转换插入生成的字节码中,但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来了可能。

2.  java泛型接口、泛型类简介

泛型类中的类型参数几乎可以用于任何可以使用接口名、类名的地方,下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分:

public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}

当声明或者实例化一个泛型的对象时,必须指定类型参数的值:

Map<String, String> map = newHashMap<String, String>();

对于常见的泛型模式,推荐的名称是:

 

K ——键,比如映射的键。

V ——值,比如 List 和 Set 的内容,或者 Map 中的值。

E ——异常类。

T ——泛型。

3. 泛型不是协变的

关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List<Object> 不是 List<String> 的父类型。

 

如果 A 扩展 B,那么 A 的数组也是 B 的数组,并且完全可以在需要 B[] 的地方使用 A[]:

Integer[] intArray = new Integer[10];
Number[] numberArray = intArray;

上面的代码是有效的,因为一个Integer 是 一个 Number,因而一个 Integer 数组是 一个 Number 数组。但是对于泛型来说则不然。下面的代码是无效的:

List<Integer> intList = newArrayList<Integer>();
List<Number> numberList = intList; //invalid

最初,大多数 Java 程序员觉得这缺少协变很烦人,或者甚至是“坏的(broken)”,但是之所以这样有一个很好的原因。如果可以将List<Integer> 赋给 List<Number>,下面的代码就会违背泛型应该提供的类型安全:

List<Integer> intList = newArrayList<Integer>();
List<Number> numberList = intList; //invalid
numberList.add(new Float(3.1415));

因为 intList 和 numberList 都是有别名的,如果允许的话,上面的代码就会让您将不是 Integers 的东西放进 intList 中。

4.  可变参数与泛型方法

泛型方法与可变参数列表能很好地共存:

package Generics;

import java.util.ArrayList;
import java.util.List;

public class GenericVarargs {
	public static <T> List<T> makeList(T... args){
		List<T> result = new ArrayList<T>();
		for(T item:args)
			result.add(item);
		return result;		 
	}
	public static void main(String[] args) {
		List ls = makeList("A");
		System.out.println(ls);
		ls = makeList("A","B","C");
		System.out.println(ls);
		ls = makeList("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""));
		System.out.println(ls);
	}
}
/*
[A]
[A, B, C]
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*/
静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
    public static<Q> void function(Q t) {
        System.out.println("function:"+t);
    }

5. 泛型的擦除

package generics;

import java.util.*;

public class ErasedTypeEquivalence {
	public static void main(String[] args) {
		Class c1 = new ArrayList<String>().getClass();
		Class c2 = new ArrayList<Integer>().getClass();
		System.out.println(c1 == c2);
	}
} /*
	 * Output: true
	 */// :~

在泛型内部,无法获得任何有关泛型参数类型的信息。

ArrayList<String>和ArrayList<Integer>是相同的类型。

6. 擦除的补偿

要想在表达式中使用类型,需要显式地传递类型的class对象。

package generics;
class Building {
}

class House extends Building {
}

public class ClassTypeCapture<T> {
	Class<T> kind;

	public ClassTypeCapture(Class<T> kind) {
		this.kind = kind;
	}

	public boolean f(Object arg) {
		return kind.isInstance(arg);
	}

	public static void main(String[] args) {
		ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(Building.class);
		System.out.println(ctt1.f(new Building()));
		System.out.println(ctt1.f(new House()));
		ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class);
		System.out.println(ctt2.f(new Building()));
		System.out.println(ctt2.f(new House()));
	}
} /*
	 * Output: true true false true
	 */// :~

7. 泛型数组为什么不能被创建

package generics;

public class Erased<T> {
	private final int SIZE = 100;

	public static void f(Object arg) {
		if (arg instanceof T) {
		} // Cannot make a static reference to the non-static type T
		T var = new T(); // Error
		T[] array = new T[SIZE]; // Error
		T[] array = (T) new Object[SIZE]; // Unchecked warning
	}
} /// :~
List<Integer>,List<String>与List在运行期并没有区别,所以List<String>放入List<Integer>并不会产生ArrayStoreException异常。但是String s=stringLists[0].get(0);将会抛出ClassCastException异常。如果允许创建泛型数组,就绕过了泛型的编译时的类型检查,将List<Integer>放入List<String>[],并在实际存的是Integer的对象转为String时抛出异常。

8. 类型通配符

类型通配符一般是使用 ? 代替具体的类型实参。注意了,此处是类型实参,而不是类型形参!且Box<?>在逻辑上是Box<Integer>、Box<Number>...等所有Box<具体类型实参>的父类。由此,我们依然可以定义泛型方法,来完成此类需求。

public class GenericTest {

    public static void main(String[] args) {

        Box<String> name = new Box<String>("corn");
        Box<Integer> age = new Box<Integer>(712);
        Box<Number> number = new Box<Number>(314);

        getData(name);
        getData(age);
        getData(number);
    }

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

}

有时候,我们还可能听到类型通配符上限和类型通配符下限。具体有是怎么样的呢?

在上面的例子中,如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制:只能是Number类及其子类。此时,需要用到类型通配符上限。

public class GenericTest {

    public static void main(String[] args) {

        Box<String> name = new Box<String>("corn");
        Box<Integer> age = new Box<Integer>(712);
        Box<Number> number = new Box<Number>(314);

        getData(name);
        getData(age);
        getData(number);
        
        //getUpperNumberData(name); // 1
        getUpperNumberData(age);    // 2
        getUpperNumberData(number); // 3
    }

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

}

此时,显然,在代码//1处调用将出现错误提示,而//2 //3处调用正常。

类型通配符上限通过形如Box<? extends Number>形式定义,相对应的,类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限正好相反,在此不作过多阐述了


Java泛型与C++模板的区别

由于架构设计上的差异,Java泛型和C++模板有如下很多不同点;

1. C++模板可以使用int基本数据类型。Java则不行,必须转而使用Integer

2. Java中,可以将模板的类型参数限定为某种特定类型。例如,你可能会使用泛型实现CardDeck,并规定参数必须扩展自CardGame。

3. C++中,类型参数可以实例化,Java不可以实例化

4. Java中,类型参数(即MyClass<Foo>中的Foo)不能用于静态方法和变量,因为他们会被MyClass<Foo>和MyClass<Bar>共享。但在C++中,这些类是不同的,类型参数可以用于静态方法和静态变量。

5. 在Java中,不管类型参数是什么,MyClass的所有实例都是同一类型。类型参数会在运行时被抹去。而C++中,参数类型不同,实例类型也不同。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值