Java泛型的特性与实现

原创 2016年05月31日 16:55:00

       面向对象的一个重要目标是对代码重用的支持。支持这个目标的一个重要的机制就是泛型机制(generic mechanism):如果除去对象的基本类型外,实现方法是相同的,纳闷我们就可以泛型实现(generic implementation)来描述这种基本的功能。例如,可以编写一个方法,将由一些项组成的数组排序;方法的逻辑关系与被排序的对象的类型无关,此时可以使用泛型方法。

       (本文所有代码均在git)


1. 利用Java5泛型特性实现泛型构件

public class Generic<AnyType> {  //指出Generic有一个类型参数
	private AnyType storedValue; 
	
	public AnyType read(){
		return storedValue;
	}
	
	public void write(AnyType x){
		storedValue = x;
	}

}

       第一行指出,Generic有一个类型参数,在Generic内部,我们可以声明泛型类型的域和使用泛型类型作为参数或返回类型的方法。例如在类Generic<String>的write方法需要一个String类型的参数。如果传递其他参数那将产生一个编译错误。

       也可以声明接口是泛型的。在Java5中,Comparable接口是泛型的,通过使类变成泛型类,以前只有在运行时才能报告的很多错误如今变成了编译时的错误。

public interface Comparable<AnyType>{
    public int compareTo(AnyType other);
}


2. 自动装箱/拆箱

       如果一个int型被传递到需要一个Integer对象的地方,那么编译器将在幕后插入一个对Integer方法构造方法的调用,这就叫做自动装箱。而如果一个Integer对象被放到需要int型量的地方,则编译器将在幕后插入一个对intValue方法的调用,这就叫做自动拆箱。

public class BoxingDemo {   //自动装箱和自动拆箱

	public static void main(String[] args) {
		Generic<Integer> g = new Generic<Integer>();
		
		g.write(37);
		int val = g.read();
		System.out.println("值为:" + val);

	}

}
注意:

       在Generic中引用的那些实体仍然是Integer对象;在Generic实例化中,int不能够代替Integer。


3. 菱形运算符

       在第2点的代码中,第4行有些烦人,因为既然g是Generic<Integer>类型的,显然创建的对象也必须是Generic<Integer>类型的,任何其他类型的参数都会产生编译错误。Java7中增加了一种新的语言特性,称为菱形运算符,使得第5行可以改写为

       Generic<Integer> g = new Generic<>();

       菱形运算符在不增加开发者负担的情况下简化了代码。 以下为带菱形运算符的Java7代码。

public class BoxingDemo {   //自动装箱和自动拆箱

	public static void main(String[] args) {
		Generic<String> g = new Generic<>();
		
		g.write("aas");
		String val = g.read();
		System.out.println("值为:" + val);

	}

}


4. 带有限制的通配符

	public static double totalArea(Collection<Shape> arr){
		double total = 0;
		
		for(Shape s:arr){
			if(s != null){
				total += s.area();
			}
		}
		return total;
	}
       上述代码中,如果传入的参数是Collection<Shape>,程序会正常运行。但是想要传入的参数是Collection<Square>时(Square extends Shape),会发现不能传入。

       The method totalArea(Collection<Shape>) in the type Generic<String> is not applicable for the arguments (Collection<Square>)

       Java5中用通配符(wildcard)来弥补这个不足。通配符用来表示参数类型的子类(或超类)。下面代码中totalArea的参数为Collection<T>,其中T is-A Shape。因此,Collection<Shape>和Collection<Square>都是可以接受的参数。通配符中还可以不带限制使用(extends Object),或不用extends而用super(来表示超类而不是子类)等。

	//通配符修正后的方法
	public static double totalArea(Collection<? extends Shape> arr){
		double total = 0;
		
		for(Shape s:arr){
			if(s != null){
				total += s.area();
			}
		}
		return total;
	}

5. 泛型static方法

       从某种意义上说,通配符修正后的totalArea方法是泛型方法,因为它能够接受不同类型的参数。但是这里没有特定类型的参数表,正如Generic类的声明中所作的那样。

       有时候特定类型很重要,原因如下:

       1.该特定类型用来做返回类型;

       2.该类型用在多于一个的参数类型中;

       3.该类型用于声明一个局部变量。


       声明一种带有若干类型参数的显式泛型方法

	//显式泛型方法
	public static <AnyType> boolean contains(AnyType[] arr, AnyType x){
		for(AnyType val:arr){
			if(x.equals(val)){
				return true;
			}
		}
		return false;
	}

6. 类型限界

       在一些方法调用中,编译器需要证明其调用是合法的,如类AnyType调用compareTo方法需满足实现Comparable接口的“条件”。此时我们可以使用类型限界(type bound)解决这个问题。类型限界在尖括号内指定,它指定参数类型必须具有的性质。

	//类型限界
    public static <AnyType extends Comparable<? super AnyType>> 
           AnyType findMax(AnyType[] arr){
        int maxIndex = 0;
        
        for(int i = 1; i < arr.length; i++){
        	if(arr[i].compareTo(arr[maxIndex]) > 0){
        		maxIndex = i;
        	}
        }
        
        return arr[maxIndex];
    }
       上述代码中,AnyType is-A Comparable<T>,其中,T是AnyType的父类。由于我们不需要知道准确的类型T,因此可以使用通配符。

7. 函数对象

       在第6点使用类型限界的findMin方法中有一个很重要的局限:它只对实现Comparable接口的对象有效,因为它使用compareTo作为所有比较决策的基础。

       此时我们需要重写findMin,使它接受两个参数:一个是对象的数组,另一个是比较函数,该函数解释如何决定两个对象中哪个大哪个小。

       一种将函数作为参数传递的独创方法是注意到对象既包含数据也包含方法,于是我们可以定义一个没有数据而只有一个类,并传递该类的一个实例。事实上,一个函数通过其放在一个对象内部而被传递。这样的对象通常叫作函数对象(function object)

public class FunctionObject {

    public static <AnyType> AnyType findMax(AnyType[] arr,
    		Comparator<? super AnyType> cmp){
    	 int maxIndex = 0;
    
    	 for(int i = 1; i < arr.length; i++){
    		 if(cmp.compare(arr[i], arr[maxIndex]) > 0){
    			 maxIndex = i;
    		 }
    	 }
         return arr[maxIndex];
    }
    
    public static void main(String[] args) {
		String[] arr = {"ZEBRA","alligator","crocodile"};
		System.out.print(FunctionObject.findMax(arr, new CaseInsensitiveCompare()));

	}


}

class CaseInsensitiveCompare implements Comparator<String>{

	@Override
	public int compare(String arg0, String arg1) {
		return arg0.compareToIgnoreCase(arg1);
	}
	
}

       使用函数对象来实现排序升序或降序的例子:

/*数据比较接口*/
interface IntCompare{
	public boolean cmp(int x,int y);
}
/*升序*/
class Cmp1 implements IntCompare{
	public boolean cmp(int x,int y){
		if(x > y){
			return true;
		}else{
			return false;
		}
	}
}
/*降序*/
class Cmp2 implements IntCompare{
	public boolean cmp(int x,int y){
		if(x > y){
			return false;
		}else{
			return true;
		}
	}
}
//选择排序实现
public class SelectSortX {

	public static void SelectSort(int[] a, IntCompare cmp){
		for(int i = 0; i < a.length; i++){
			int min = i;
			for(int j = i + 1; j < a.length; j++){
				if(cmp.cmp(a[min], a[j])){
					min = j;
				}
				if (i != min){
					swap(a, i, min);
				}
			}
		}
	}
	
	public static void swap(int[] a, int x, int y){
		int temp = a[x];
		a[x] = a[y];
		a[y] = temp;
	}
	
	
	public static void main(String[] args) {
		int[] array = {4,2,1,6,3,6,0,-5,1,1};
//		SelectSort(array, new Cmp1());      //升序排序
		SelectSort(array, new Cmp2());      //降序排序
		for(int i = 0; i < array.length; i++){
			System.out.printf("%d ",array[i]);
		}

	}

}


参考资料:

1. 《数据结构与算法分析》


版权声明:本文为博主原创文章,若要转载请注明文章出处。

相关文章推荐

Spring4新特性——更好的Java泛型操作API

Spring4新特性——泛型限定式依赖注入 Spring4新特性——核心容器的其他改进 Spring4新特性——Web开发的增强 Spring4新特性——集成Bean Validat...

黑马程序员——java5特性之枚举和泛型概述

------------------------------------------android培训     java培训   期待与您交流! --------------------------...

java高级特性之泛型与通配符

参考传智播客视频 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型...

Java 8新特性探究(六): 泛型的目标类型推断

简单理解泛型泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。通俗点将就是“类型的变量”。这种类型变量可以用在类、接口和方法的创建中。理解Java...

Java基础18--泛型--工具类--JDK1.5新特性

18-1,泛型-概述 1,泛型是JDK1.5出现的新技术,新技术的出现是为了解决问题。 2,泛型可以用于明确一个集合中存储什么类型的元素 ArrayList al = new ArrayList();...

黑马程序员_java基础加强_jak1.5新特性(3注解与)泛型概念

---------------------- android培训、java培训、期待与您交流! ---------------------- 一:注解 1:注解 注解就是告诉编译器在变异那些...

Java-1.5新特性之泛型

一、泛型由来 Java语言类型包括八种基本类型(byte short int long float double boolean char)和复杂类型,复杂类型包括类和数组。 早期Java版本(1.4...

java中的<?><T><E>详解Jdk5.0新特性Generic Types (泛型)

java中的详解Jdk5.0新特性Generic Types (泛型) 1. 介绍2.定义简单Java泛型 其实Java的泛型就是创建一个用类型作为参数的类。就象我们写类的方法一样,方法是这样...

黑马程序员———java高级特性之泛型

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流------ 一.泛型的内部原理 1.1...

Java1.5新特性之泛型

在JDK1.5之前我们使用集合都是没有类型限制了
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java泛型的特性与实现
举报原因:
原因补充:

(最多只允许输入30个字)