Java泛型

本文详细介绍了Java泛型的概念,通过示例展示了泛型在类、接口和方法中的应用,解释了泛型如何增强类型安全性,避免了运行时的类型转换问题。同时,讨论了泛型的子类、通配符的使用,以及泛型数组的创建。此外,还以Comparator为例,说明了泛型在排序中的作用,展示了自定义比较器进行降序排序的实现。
摘要由CSDN通过智能技术生成

泛型


#泛型的概念

Java泛型(generics)是JDK 5中引入的一个新特性,泛型的本质是参数化类型,也就是说在定义类、接口和方法时,其所操作的数据类型被指定为一个参数,分别称为泛型类、泛型接口、泛型方法。声明的类型参数在使用时用具体的类型来替换,这就像方法中的形参和实参之间的关系。

假设有下面三个类的定义:

class Fruit{}

class Apple extends Fruit{}

class Orange extends Fruit{}

某段代码如下:

//List是一个接口,用来表示某种有序的集合。可以对列表中每个元素的插入位置进行精确地控制。
//而且可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
//ArrayList是大小可变的数组对List接口的实现
//我们创建一个box用来专门存放Apple
List box = new ArrayList();//使用add在Box中所添加的任何对象都会转换为Object对象而存放
box.add(new Apple());//在Box中插入一个Apple对象
box.add(new Orange());//在Box中是可以插入一个Orange对象的


Apple apple = box.get(0);//get方法获取到的对象为Object对象,因此,本条语句会出错,如下图
apple = (Apple)box.get(0);
apple = (Apple)box.get(1);

img 代码经过如下修正:

Apple apple = (Apple)box.get(0);//get方法获取到的对象为Object对象,这里经过了强制转换,如下图:
apple = (Apple)box.get(0);
apple = (Apple)box.get(1);//在第1个位置,我们前面代码中放的是一个Orange对象,在运行时会出错

img 运行结果如下: img 我们从运行结果可以看到上述的代码在编译的时候是无法防止box中放入其他种类的水果的,只能在运行的时候才能发现类型的转换问题,此时是不是已经晚了?!

如果我们把上面的代码用泛型来实现,如下:

//List接口的定义原型为List<E>,其中E就是一个形式上类型参数
//List<Apple>中的Apple为实际类型
List<Apple> box = new ArrayList<Apple>();
box.add(new Apple());
box.add(new Orange());//此时再加入Orange对象则会在编译的时候就能发现错误,见下图:

Apple apple = box.get(0);//因为box的类型为List<Apple>,所以get方法的返回值类型为Apple类型,而不是Object类型

img 从上面我们看到,使用泛型以后,我们再在box中加入其他非Apple的对象就在编译的时候就得到了检查。

如果上例改成如下的形式呢?

List<Fruit> box = new ArrayList<Fruit>();
box.add(new Apple());
box.add(new Orange());

通过编译及运行,我们看到完美通过。那若再改成下面的代码呢:

List<Fruit> fruitBox; 
List<Apple> appleBox = new ArrayList<Apple>();
fruitBox = appleBox;//此句会产生错误,

上面的代码再编译时会产生下面的错误: img 我们可以了解到List<Fruit>List<Apple>是完全不同的两个类型。

#泛型的定义

类型参数的命名风格为:用简练的名字作为形式类型参数的名字(如果可能,单个字符),最好避免小写字母,比如:T、E等。类型参数表用尖括号括起来,多个类型参数之间用逗号隔开。泛型的定义可以应用在接口、类、方法。 示例如下:

interface A<T,E>{//定义泛型接口、类,类型参数表在接口名、类名之后
	public <T,E> T getT(T t,E e);//类型参数表在存取权限之后
}

class B<T,E> implements A<T,E>{
	public <T,E> T getT(T t,E e){
		return t;
	}
}

class TestGenerics{
	public static void main(String[] args) {
		B<String,Integer> b = new B<String,Integer>();//
		System.out.println(b.getT("abc",1));
	}
}

#泛型的子类

在前面的例子中,我们已经看到了List<Fruit>List<Apple>是完全不同的两个类型。再通用些,如果类型A是类型B的子类型,那C<A>C<B>之间没有任何关系。有没有可能将C<A>类型的实例赋给一个C<B>类型的变量呢?答案是可以,只不过需要改一改C<B>的形式。我们定义类型C<? extends B>,其中?表示某个类继承自(实现)B。我们将前面的代码经过改造如下:

List<? extends Fruit> fruitBox; 
List<Apple> appleBox = new ArrayList<Apple>();
fruitBox = appleBox;

则代码无错通过,其中List<? extends Fruit>可以看作是List<Apple>的父类。

有没有可能将C<B>类型的实例赋给一个C<A>类型的变量呢?答案也是可以的,我们仍然需要改一下C<A>的形式。我们定义类型C<? super A>,其中?表示A继承(实现)自它。

List<Fruit> fruitBox = new ArrayList<Fruit>(); 
List<? super Apple> appleBox = fruitBox;

代码无错通过,其中List<? super Apple>可以看作是List<Fruit>的子类。

#通配符

通配符?除了可以用在上述的泛型子类的场合,它还可以表示任意实际类型参数,示例:

class Info< T> {
	private T var ;  // 定义泛型变量
	public void setVar(T var) {
		this.var = var ;
	}
	public T getVar() {
		return this.var ;
	}
	public String toString() { // 直接打印
		return this.var.toString() ;
	}
};
public class GenericsDemo {
	public static void main(String args[]) {
		Info< String> i = new Info< String>() ;  // 使用String为泛型类型
		i.setVar("it") ;       // 设置内容
		fun(i) ;
	}
	public static void fun(Info< ?> temp) { // 可以接收任意的泛型对象
		System.out.println("内容:" + temp) ;
	}
};

#泛型数组

示例

public class GenericsDemo1<T> {
	public static void main(String args[]) {
		Integer integer[] = fun1(1, 2, 3, 4, 5, 6) ; // 返回泛型数组
		fun2(integer) ;
	}
	@SafeVarargs//如果把这个标记去掉,会产生如下图所示的警告
	public static <T> T[] fun1(T...arg) { //这种定义形式可以将实参传递过来的参数组成数组
		return arg ;   // 返回泛型数组
	}
	public static <T> void fun2(T param[]) { // 输出
		System.out.print("接收泛型数组:") ;
		for (T t : param) {
			System.out.print(t + "、") ;
		}
	}
};

img

#泛型示范

#Comparator

前述的Arrays中的普通的sort方法为升序排序,但也提供了一个可以使用Comparator的排序,利用Comparator既可以升序也可以降序排序。

public interface Comparator<T>可以看到,它是一个接口,它在java.util包中,其中有两个方法,分别为:

int compare(T o1, T o2)
boolean equals(Object obj)

第二个方法在实现Comparator的类中,一般不需要实现,这是因为所有类都继承自Object,而Object中有equals方法,可以视同对Comparatorequals的实现。 我们需要实现的是compare方法,如果参数的类型不允许此 Comparator 对它们进行比较,则会抛出ClassCastException

该方法的实现比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。 设符号 sgn(expression) 表示符号数学函数,根据 expression 的值为负数、0 还是正数,该函数分别返回 -1、0 或 1。实现程序必须确保对于所有的 x 和 y 而言,都存在 sgn(compare(x, y)) == -sgn(compare(y, x))。(这意味着当且仅当 compare(y, x) 抛出异常时 compare(x, y) 才必须抛出异常。)

实现程序还必须确保关系是可传递的:((compare(x, y)>0) && (compare(y, z)>0)) 意味着 compare(x, z)>0。

最后,实现程序必须确保 compare(x, y)==0 意味着对于所有的 z 而言,都存在 sgn(compare(x, z))==sgn(compare(y, z))。

虽然这种情况很普遍,但并不 严格要求(compare(x, y)==0) == (x.equals(y))。一般说来,任何违背这个条件的 Comparator 都应该清楚地指出这一事实。推荐的说法是“注意:此 Comparator 强行进行与 equals 不一致的排序。”

示例:

import java.util.Random;
import java.util.Arrays;
import java.util.Comparator;

class TestComparator{
	//顶一个Integer的数组
	static Integer[] intArray = new Integer[10];
	//对该数组进行初始化
	static{
		Random random = new Random();
		for(int i = 0; i < intArray.length; i++){
			intArray[i] = random.nextInt(10);
		}
	}

	public static void main(String[] args) {
		//先输出未排序的数组状态
		for(int x:intArray){
			System.out.print(x + " ");
		}
		//通过使用Comparator对数组进行降序排序
		Arrays.sort(intArray, new Comparator<Integer>(){
			public int compare(Integer t1, Integer t2){
				return t2 - t1;//t1>t2,返回负值,反之。它的返回值就使得排序的结果为降序
			}
		});

		System.out.println();
		//输出排序后的数组状态
		for(int x:intArray){
			System.out.print(x + " ");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值