`
liaobinxu
  • 浏览: 43332 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

泛型和方法

阅读更多
如何在java类中一些通用方法, 特别是一些静态的工具方法?

比如,数组Arrays的sort、search等?

1. 整数数组的顺序收缩
	public static int seqSearch(int[] arr, int first, int last, int target) {
		for (int i = first; i < last; i++)
			if (arr[i] == target)
				return i;

		return -1;
	}

1.1对上面的方法进行抽象,最先让我们想到就是,使用java的Object的引用,来实现通用方法
	public static int seqSearch(Object[] arr, int first, int last, Object target) {
		for (int i = first; i < last; i++)
			if (arr[i].equals(target))
				return i;

		return -1;
	}

2.使用泛型来改进通用方法
2.1这样看来好像Object的引用好像非常方便,而且第二个顺序搜索可以使用到float,double,String等等。如果我们要进一步研究就会出现问题
	public static void selectionSort(int[] arr) {
		int n = arr.length, smallIndex = 0;
		for (int i = 0; i < n; i++) { // 遍历array数组
			smallIndex = i;
			for (int j = i + 1; j < n; j++)
				if (arr[smallIndex] > arr[j]) // 选择最小的索引j
					smallIndex = j;
			// if (smallIndex != i) {
			exchange(arr, i, smallIndex);// 交换array[i]与 min(array[i+1,..,n])
			// }
		}
	}

2.2上面的代码是一个顺序排序的算法,若果我们要写一个通用方法,就必须把object类型强制装换为一个实现Comparable接口的方法。
引用
JVM在处理类型强制装换的时候就会,抛出一个警告: uncheck cast

	@SuppressWarnings("unchecked")
	public static void selectionSort(Object[] arr) {

		int n = arr.length, smallIndex = 0;
		for (int i = 0; i < n; i++) { // 遍历array数组
			smallIndex = i;
			for (int j = i + 1; j < n; j++)
				if (((Comparable<Object>)arr[smallIndex]).compareTo(((Comparable<Object>)arr[j])) > 0) // 选择最小的索引j
					smallIndex = j;
			// if (smallIndex != i) {
			exchange(arr, i, smallIndex);// 交换array[i]与 min(array[i+1,..,n])
			// }
		}
	}


有此可以看出使用Object引用来处理通用问题,当使用实参如果没有实现Comparable接口,编译器会抛出一个castClassException的运行时异常。这样的程序是不安全的。
3比较Object引用和泛型
3.1使用Object引用来泛化一个算法(如顺序搜索)。通过使用数组的Object引用和目标值, 只要数据类型实现了equals方法,算法中要比较大小的数据类必须实现Comparable接口, 现在我们来用java泛型来解决这个问题
	public static <T extends Comparable<? super T>> void selectionSort(T[] arr){
		int n = arr.length;
		int smallIndex;
		for (int i = 0; i < n-1; i++) {
			smallIndex=i;
			for (int j = i+1; j < n; j++) 
				if (arr[j].compareTo(arr[smallIndex])<0) 
					smallIndex=j;
			exchange(arr, smallIndex, i);
		}
	}


引用
在Arrays类中的静态方法 selectionSort(),这个方法处理的是整数类型,现在我们要用泛型范本版本来实现这个算法, 由于要泛型类型数组T[]中的两个元素要进行比较,所以传递实参的对象类型或其超类必须实现Comparable借口

泛型的使用javac知道了需要检查输入实参或实参对象的超类必须实现Comparable接口,才能正确调用
4.下面来介绍Java泛型的使用和思想
4.1泛型集合
  考虑到基于Object的集合局限性,我们需要寻求一种新的解决方案来解决创建通用结构的为您提。 首先,我们希望通用结构是类型安全的(type safe), 简单说来就是指编译器应该标识出对象类型的任何错误使用,从而能够避免在运行时发现这样的错误。 通过这种方式,软件在发布之前会表示对象的错误使用问题, 而且能够避免来自客户端的讨厌的和令人为难的应用。此外,我们还希望通过减少对类型强制转换的要求来显著地简化应用程序中集合的使用。 Java泛型(java generic)提供了这样的解决方案。 在创建就集合对象时指定了类型(该类型规定了集合中存储元素的种类)。编译器不允许使用具有无效类型时的元素来更新集合, 并且还在可以识别在集合中的任意元素的类型
引用

泛型类允许编译器类型检查类型,并且避免使用类型强制转换。

4.2泛型类:
public class Store<T> {
	private T value;// 被保存的变量value引用
	public Store(T v) {// 构造器使用泛型类型实参创建一个实例
		value = v;
	}
       public T getValue() {
		return value;
	}
	public void setValue(T value) {
		this.value = value;
	}
	public String toString() {
		return "value = " + value;
	}

引用
在类名后面的尖括号中扩入某个泛型类型就能够生成了这个泛型类

4.3泛型接口
接口也可是泛型结构。与泛型类一样, 接口头部中使用一个类型参数, 并且这个类型能够用于方法和返回类型。 例如, 我们定义Accumulator<T>接口, 这个接口具有单个方法add(), 该方法为参数V使用的方形类
public interface Accumulator <T> {
	/**
	 * 形式参数V的类型T,要和接口头部定义的泛型类型一样, 否者编译会出现错误
	 * @param v
	 */
	public void add(T v);
}

引用
接口也可是泛型结构。实现泛型接口时, 需要提供实际的类型

实现一:AccumulatorNumber
public class AccumulatorNumber implements Accumulator<Number> {
	private double total;
	
	public AccumulatorNumber(){
		total=0.0;
	}
	public double getTotal() {
		return total;
	}
	@Override
	public void add(Number v) {
		total+=v.doubleValue();
	}

}

实现二:
public class AccumulatorTime implements Accumulator<Time24> {
	private Time24 total;	
	public AccumulatorTime(){
		total=new Time24();
	} 	
	public Time24 getTotal() {
		return total;
	}
	@Override
	public void add(Time24 v) {
		total.addTime(v.getTime()*60+v.getMinute());
	}

}

泛型接口和其实现的测试用例
public class AccumulatorTestCase extends TestCase {
	/**
	 * 测试: AccumulatorTime 实例能否正常实现泛型接口Accumulator<Time24>
	 */
	public void accumulatorTime(){
		AccumulatorTime time=new AccumulatorTime();		
		time.add(new Time24(8,30));
		time.add(new Time24(2,30));		
		System.out.println(time.getTotal());
		}
	
	/**
	 * 测试: AccumulatorNumber 实例能否正常实现泛型接口Accumulator<Number>
	 */
	public void accumulatorNumber(){
		AccumulatorNumber  number=new AccumulatorNumber();
				number.add(1.1);
		number.add(5);		
		System.out.println(number.getTotal());		
	}

}

4.4泛型方法
我们使用类型参数创建泛型类和接口。使用相同的参数也可以用来创建泛型方法。 我们使用一个简单的示例,求比较两个实参的大小
引用
方法可以是泛型结构, 这允许你创建用于搜索、排序的范型方法

4.4.1基本的范型方法
	@SuppressWarnings("unchecked")
	public static <T> T max1(T objA, T objB){
		if (((Comparable<T>)objA).compareTo(objB)>0) 
			return objA;
		return objB;
	}


在泛型方法的java处理中, 类型参数在运行时被传递给方法. 编译器不会验证方法Comparable<T>的类型, 会给出一个警告"uncheck cast". 因此, if语句中需要显示的类型强制转换为Comparable<T>的类型.类型参数在运行时被传递的行为具有其他的影响,即使类型T没有实现T没有实现Comparable接口, 方法max()也可以包含在源代码中. 此时, 编译器会发出一个"classCastException"异常.
* @param <T>  泛型类型T
* @param objA 比较的大小的一个对象
* @param objB 比较的大小的另外一个对象
* @return 最大值
引用
在max()第一个版本中, 如果类型没有实现Comparable接口,那么会出现一个ClassCastException异常

4.4.2为泛型类型使用绑定
	public static <T extends Comparable<T>> T max2(T objA,T objB){
		if (objA.compareTo(objB)>0) 
			return objA;
		return objB;
	}

为泛型类型使用绑定, 为了更好的改进类型检查, 需要编译必须检查类型实参,从而确保这个类型实现Comparable<T>. 通过使用标示符<T extends Comparable<T>>能够完成类型实参的检查. 这个方法min()方式是第二个版本, 类型T必须实现Comparable<T> , 这个借口不能在超类中实现 * @param <T>  泛型类型T
* @param objA 比较的大小的一个对象
* @param objB 比较的大小的另外一个对象
* @return 最大值
4.4.3泛型和继承一起使用
	public static <T extends Comparable<? super T>> T max3(T objA,T objB){
		if (objA.compareTo(objB)>0) 
			return objA;
		return objB;
	}

为了处理能够将超类或子类类型作为实参传递给泛型类和方法引入通配符.语法<?>提示类型正在处理的是未知类型
public static <T extends Comparable<? super T>> T fun(T t1,T t2)

引用
通配符"?"和绑定"super T" 会指示编译器验证类型T或T的某些超类是否实现了Comparable接口,这个第三个版本,也是最通用的形式.这个方法min()方式是第二个版本, 类型T必须实现Comparable<T> , 这个借口不能在超类中实现

4.4.4前面三个方法测试用例:
	/**
	 * 这个第一个版本泛型方法	
	 */
public void max1() {
		int a = 0, b = 1;
		System.out.println(GenericUtil.max(a, b));// Output:1(b)

		Integer a1 = -5, b2 = 5;
		System.out.println(GenericUtil.max(a1, b2));// Output:5(b2)

		String s1 = "abc", s2 = "bbc";
		System.out.println(GenericUtil.max(s1, s2));// Output:bbc(s2)

		String s3 = "abc", s4 = "abb";
		System.out.println(GenericUtil.max(s3, s4));// Output:abc(s3)

		// Error: java.lang.ClassCastException: ds.demo.Rectange cannot be cast
		// to java.lang.Comparable
		// 因为Rectange没有Comparable<T>接口给objA强制转换类型时, 抛出异常
		System.out.println(GenericUtil.max1(new Rectange(5, 4), new Rectange(5,
				4)));
	}

	/**
	 * 这个第二个版本泛型方法	
	 */
	public void max2() {
		String s1 = "abc", s2 = "bbc";
		System.out.println(GenericUtil.min(s1, s2));// Output:abc(s1)

		
		//如果两个实参没有实现Comparable<T>编译器报错:Bound mismatch
		System.out.println(GenericUtil.max2(new Employee("liao", "123-45-678"), new Employee("sun",
				"123-55-678"))); //Employee= liao \n SSN= 123-45-678
	}
	/**
	 * 这个第三个版本泛型方法
	 * 
	 *测试: GenericUtil#<T> boolean equals(T, T)方法
	 *
	 *通配符"?"和绑定"super T" 会指示编译器验证类型T或T的某些超类是否实现了Comparable接口,这个第三个版本,也是最通用的形式.这个方法min()方式是第二个版本,
	 * 类型T必须实现Comparable<T> , 这个借口不能在超类中实现
	 */
	public void max3() {
		String s1 = "abc", s2 = "bbc";
		System.out.println(GenericUtil.equals(s1, s2));// Output:abb(s2)

		
		//如果两个实参没有实现Comparable<T>编译器报错:Bound mismatch
		System.out.println(GenericUtil.max3(new Employee("liao", "123-45-678"), new HourlyEmployee("sun",
				"123-55-678",7.8,8.2))); //Employee= liao \n SSN= 123-45-678
		
		System.out.println("**************************");
		System.out.println(GenericUtil.equals( new HourlyEmployee("sun",
				"123-55-678",7.8,8.2),new SalaryEmployee("liao", "123-45-678",3300.3))); //Employee= liao \n SSN= 123-45-678
	}

5.我们对泛型的使用的扩展实例
Entry类具有建字段和值字段的不同实例变量。 这个类的方法允许只访问键字段。不允许访问和更新键字段
public class Entry {
	private int key;
	private int value;	
	public Entry(int key, int value){
		this.key=key;
		this.value=value;
	}
	public int getKey() {
		return key;
	}
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public String toString(){
		return "key= "+key+" value= "+value;
	}
}

修改Entry类, 使其成为一个泛型, 并且储存类型K的变量Key以及类型V的变量value。泛型类型K和V可以不同。 例如K=String,V=Integer。为了在一个类中包括两个泛型类,需要在类名之后放置泛型<K,V>。Entry类实现方equals(Object obj)和Comparable接口
public class Entry<K extends Comparable<? super K>, V> implements
		Comparable<Entry<K, V>> {
	private K key;
	private V value;
	public Entry(K key, V value) {
		this.key = key;
		this.value = value;
	}
	public K getKey() {
		return key;
	}
	public V getValue() {
		return value;
	}
	public void setValue(V value) {
		this.value = value;
	}
	public String toString() {
		return "key= " + key + " value= " + value;
	}
	@Override
	public int compareTo(Entry<K, V> o) {
		int keyCompareTo = key.compareTo(o.key);
		
		if (keyCompareTo > 0)
			return 1;
		else if (keyCompareTo < 0)
			return -1;
		else
			return 0;
	}
	@SuppressWarnings("unchecked")
	public boolean equals(Object o){
		if(o instanceof Entry)
			if(key.compareTo(((Entry<K, V>)o).key)==0)
				return true;
		return false;
	}
}
0
0
分享到:
评论

相关推荐

    C#泛型类、泛型方法、泛型接口、泛型委托的实例

    本文将深入探讨泛型类、泛型方法、泛型接口和泛型委托,并通过实例来阐述它们的应用。 首先,我们来看泛型类。泛型类是具有一个或多个类型参数的类。类型参数是在定义类时使用的占位符,实际的类型在创建类的实例时...

    【Flutter】Dart 泛型 ( 泛型类 泛型方法 特定类型约束的泛型 ).zip

    【Flutter】Dart 泛型 ( 泛型类 | 泛型方法 | 特定类型约束的泛型 ) https://hanshuliang.blog.csdn.net/article/details/114059611 博客源码快照

    java 泛型方法使用示例

    例如,一个接受两个相同类型参数并返回它们之和的泛型方法可以这样定义: ```java public &lt;T&gt; T sum(T a, T b) { return a + b; } ``` 在这个例子中,`&lt;T&gt;` 是类型参数,代表某种未知的数据类型,`T` 在方法体内...

    c#泛型类、泛型方法、泛型接口、泛型委托

    泛型主要分为四个关键部分:泛型类、泛型方法、泛型接口和泛型委托。下面将详细介绍这四个方面。 1. 泛型类: 泛型类是具有一个或多个类型参数的类。这些类型参数是占位符,代表一种未知的数据类型,直到在创建类...

    Generic_2(泛型类-泛型方法-泛型接口-泛型限定(上限)

    这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”...

    泛型 反射 相关概念方法使用

    java.lang.reflect包提供了Constructor、Method和Field类,分别代表了类的构造器、方法和字段,可以用来执行相应的操作。 泛型和反射的结合使用可以实现更灵活的编程。例如,在某些情况下,我们可能需要动态地创建...

    java泛型,java知识

    - 泛型(Generics)允许在定义类、接口和方法时声明类型参数,从而创建泛型类、泛型接口和泛型方法。这使得代码可以处理多种类型的对象,而不必为每种类型都创建单独的类或方法。 - 类型参数通常以大写字母表示,...

    泛型方法的使用.rar

    泛型方法是泛型的一种应用形式,它提高了代码的灵活性、类型安全性和性能。在本教程中,我们将深入探讨C#中的泛型方法的使用。 首先,让我们理解什么是泛型方法。泛型方法是在定义方法时引入了一个或多个类型参数,...

    c#方法异常和泛型

    在C#编程中,方法异常和泛型是两个至关重要的概念。它们构成了C#语言功能的核心部分,并在软件开发中发挥着关键作用。理解和熟练掌握这两个主题,将有助于提升你的编程技能,无论你是初学者还是有经验的开发者。 ...

    Java中的泛型方法演示代码

    在这个“Java中的泛型方法演示代码”中,我们可以期待看到如何在Java中定义和使用泛型方法的实例。 首先,泛型方法的基本语法是在方法声明前加上尖括号`&lt;&gt;`,并在其中定义一个或多个类型参数。例如: ```java ...

    使用泛型的方法.java

    泛型是程序设计语言的一...各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。

    [Java泛型和集合].

    在Java中,泛型是一种在类、接口和方法中引入类型参数的技术。它们允许我们在编写代码时指定一个或多个类型,而这些类型可以在实际使用时进行替换。这带来了两大优势:编译时类型检查和消除类型转换的需要。例如,`...

    泛型和泛型集合类用法

    上述代码定义了一个名为`ItemList&lt;T&gt;`的泛型类,它可以存储任何类型的对象,并提供了添加和获取对象的方法。这种自定义泛型类可以根据具体的业务需求灵活扩展功能。 通过以上分析,我们可以看出泛型和泛型集合类在...

    Dart中的泛型 泛型方法 泛型类 泛型接口、库 自定义库、系统库、第三方库.zip

    泛型涉及到泛型方法、泛型类、泛型接口以及库的使用,这些概念是理解Dart中类型系统的关键部分。下面将详细阐述这些知识点。 1. **泛型方法**:泛型方法允许我们在方法签名中使用类型参数,这样方法可以处理不同...

    实例187 - 泛型化方法与最小值

    泛型化方法与最小值是Java...通过定义泛型方法来找到列表中的最小值,我们可以处理任何实现了`Comparable`接口的对象,从而提高代码的复用性和可维护性。在实际项目中,充分利用泛型可以极大地提升代码的效率和质量。

    【Java基础】泛型方法 - 右撇子 - 博客频道 - CSDN.NET

    【Java基础】泛型方法 - 右撇子 - 博客频道 - CSDN.NET

    gson解析泛型和将泛型转为json字符串

    泛型允许我们在类、接口和方法中使用类型参数,从而提高了代码的类型安全性和重用性。当我们使用Gson与泛型结合时,可以更方便地处理各种类型的对象,无论它们是基本类型还是自定义类。 1. **Gson与泛型解析** 当...

Global site tag (gtag.js) - Google Analytics