Java学习day054 定义简单泛型类、泛型方法、类型变量的限定

使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。

day054   定义简单泛型类、泛型方法、类型变量的限定


1.定义简单泛型类

一个泛型类(generic class)就是具有一个或多个类型变量的类。下面使用一个简单的Pair类作为例子。对于这个类来说,我们只关注泛型,而不会为数据存储的细节烦恼。下面是Pair类的代码:

/**
 *@author  zzehao
 */

public class Pair<T>
{
	private T first;
	private T second;

	public Pair() { first = null; second = null; }
	public Pair(T first, T second) { this.first = first; this.second = second; }
	public T getFirst() { return first; }
	public T getSecond() { return second; }
	public void setFirst(T newValue) { first = newValue; }
	public void setSecond(T newValue) { second = newValue; }
}

Pair类引人了一个类型变量T,用尖括号(<>)括起来,并放在类名的后面。泛型类可以有多个类型变量。例如,可以定义Pair类,其中第一个域和第二个域使用不同的类型:

public class Pair<T,U>{...}

类定义中的类型变量指定方法的返回类型以及域和局部变量的类型。例如,

private T first;//uses the type variable

用具体的类型替换类型变量就可以实例化泛型类型,例如:

Pair<String>

可以将结果想象成带有构造器的普通类:

Pair<String>()
Pair<String>(String, String)

和方法:

String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)

换句话说,泛型类可看作普通类的工厂。

下面的程序使用了Pair类。静态的minmax方法遍历了数组并同时计算出最小值和最大值。它用一个Pair对象返回了两个结果。回想一下compareTo方法比较两个字符串,如果字符串相同则返回0;如果按照字典顺序,第一个字符串比第二个字符串靠前,就返回负值,否则,返回正值。

/**
 *@author  zzehao
 */

public class PairTest1
{
	public static void main(String[] args)
	{
		String[] words = { "Mary", "had", "a", "little", "lamb" };
		Pair<String> mm = ArrayAlg.minmax(words);
		System.out.println("min = " + mm.getFirst());
		System.out.println("max = " + mm.getSecond());
	}
}

class ArrayAlg
{
	public static Pair<String> minmax(String[] a) 
	{
		if (a == null || a.length == 0) return null; 
		String min = a[0];
		String max = a[0]; 
		for (int i =1 ;i < a.length; i++)  
		{
			if (min.compareTo(a[i])>0) min = a[i];
			if (max.compareTo(a[i])<0) max = a[i];
		}
		return new Pair<>(min,max);
	}
}

运行的结果是:

                  


2.泛型方法

前面已经介绍了如何定义一个泛型类。实际上,还可以定义一个带有类型参数的简单方法。

class ArrayAlg
{
    public static<T> T getMiddle(T... a)
    {
         return a[a.length /2];
    }
}

这个方法是在普通类中定义的,而不是在泛型类中定义的。然而,这是一个泛型方法,可以从尖括号和类型变量看出这一点。注意,类型变量放在修饰符(这里是public static)的后面,返回类型的前面。

泛型方法可以定义在普通类中,也可以定义在泛型类中。

当调用一个泛型方法时’在方法名前的尖括号中放人具体的类型:

String middle =ArrayAlg.<String>getMiddle("]ohnM, "Q.","Public");

在这种情况(实际也是大多数情况)下,方法调用中可以省略类型参数。编译器有足够的信息能够推断出所调用的方法。它用names的类型(即String[])与泛型类型T[]进行匹配并推断出T一定是String。也就是说,可以调用

String middle =ArrayAlg.getHiddle("]ohn","Q.","Public");

几乎在大多数情况下,对于泛型方法的类型引用没有问题。偶尔,编译器也会提示错误,此时需要解译错误报告。看一看下面这个示例:

double middle = ArrayAlg.getMiddle(B.14, 1729, 0);

错误消息会以晦涩的方式指出(不同的编译器给出的错误消息可能有所不同):解释这句代码有两种方法,而且这两种方法都是合法的。简单地说,编译器将会自动打包参数为1个Double和2个Integer对象,而后寻找这些类的共同超类型。事实上;找到2个这样的超类型:Number和Comparable接口,其本身也是一个泛型类型。在这种情况下,可以采取的补救措施是将所有的参数写为double值。


3.类型变量的限定

有时,类或方法需要对类型变量加以约束。下面是一个典型的例子。我们要计算数组中的最小元素:

class ArrayAIg
{
    public static <T> T iin(T[] a) //almost correct
    {
        if (a==null||a.length = 0) return null;
        T smallest = a[0];
        for (int i =1 ; i < a.length; i++)
            if (smallest.compareTo(a[i]) > 0) smallest = a[i];
        return smallest;
    }
}

但是,这里有一个问题。请看一下min方法的代码内部。变量smallest类型为T,这意味着它可以是任何一个类的对象。怎么才能确信T所属的类有compareTo方法呢?

解决这个问题的方案是将T限制为实现了Comparable接口(只含一个方法compareTo的标准接口)的类。可以通过对类型变量T设置限定(bound)实现这一点:

public static <T extends Coiparab 1e> T min(T[] a) . . .

实际上Comparable接口本身就是一个泛型类型。目前,我们忽略其复杂性以及编译器产生的警告。

现在,泛型的min方法只能被实现了Comparable接口的类(如String、LocalDate等)的数组调用。由于Rectangle类没有实现Comparable接口,所以调用min将会产生一个编译错误。

在此为什么使用关键字extends而不是implements?毕竟,Comparable是一个接口。下面的记法

<T extends BoundingType 〉

表示T应该是绑定类型的子类型(subtype)。T和绑定类型可以是类,也可以是接口。选择关键字extends的原因是更接近子类的概念,并且Java的设计者也不打算在语言中再添加一个新的关键字(如sub)。

一个类型变量或通配符可以有多个限定,例如:

T extends Comparable & Serializable

在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。

在下面的的程序中,重新编写了一个泛型方法minmax。这个方法计算泛型数组的最大值和最小值,并返回Pair。

/**
 *@author  zzehao
 */
import java.time.*;

public class PairTest2
{
	public static void main(String[] args)
	{
		LocalDate[] birthdays =
			{
				LocalDate.of(1906, 12, 9),//C. Hopper
				LocalDate.of(1815, 12, 10),//A. Lovelace
				LocalDate.of(1903, 12, 3),//3. von Neumann
				LocalDate.of(1910, 6, 22),//K. Zuse
			};
		Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
		System.out.println("min = " + mm.getFirst());
		System.out.println("max = " + mm.getSecond());
	}
}

class ArrayAlg
{
	public static <T extends Comparable> Pair<T> minmax(T[] a) 
	{
		if (a == null || a.length == 0) return null; 
		T min = a[0];
		T max = a[0]; 
		for (int i =1 ;i < a.length; i++)  
		{
			if (min.compareTo(a[i])>0) min = a[i];
			if (max.compareTo(a[i])<0) max = a[i];
		}
		return new Pair<>(min,max);
	}
}

运行的结果:


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值