java基础:14.1 泛型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/L20902/article/details/88878731

1、回顾ArrayList类

ArrayList是一种泛型类,具有一个泛型类型<E>,创建一个ArrayList 时,可以指定一个具体的类型来替换E。Java 提供ArrayList 类来存储不限定个数的对象。位于java.util 包中。存储在ArrayList中的元素必须是一种对象,不能使用int的基本数据类型来代替一个泛型类.

ArrayList<T> cities = new ArrayList<>() ; T可以是引用类型 例如,string,Intege,Double 或Characterr…;不能使用int 、double 或char 这样的基本类型

数组和ArrayList的区别
— 旦创建了一个数组,它的大小就确定下来了。可以使用方括号访问数组元素(例如:a[index]). 当创建ArrayList 后,它的大小为0。如果元索不在数组列表中,就不能使用get(index)和set(index.element)方法。向数组列表中添加、插人和删除元素是比较容易的,而向数组中添加、插人和删除元素是比较复杂的。为了实现这些操作,必须编写代码操纵这个数组。注意,可以使用;java.util.Arrays.sortUrray)方法来对一个数组排序。如果要对一个数组列表排序,使用java.util.Collections.sort(arrayList)方法。

自动拆箱
如果元素是包装类型,例如, Integer , Double 或Character ,那么可以直接将这个元素赋给一个基本类型的变量。这个过程称为自动拆箱。

ArrayList<Double> list = new ArrayList<>();
list.add(5.5);   // 5.5 is automatlcally converted to new Double(5.5)
list. add (3.0);  // 3. 0 i s automati ca 11 y converted to new Ooub 1 e (3 .0)
Double doubleObject = list.get(O); // No casting is needed
double d = list.get(l); // Automatically converted to double

2、泛型的概念

泛型类型 <E>

泛型的用法是在容器后面添加,Type可以是类,抽象类,接口。

ArrayList heros = new ArrayList<Hero>(); // 只能存放Hero对象及子类

ArrayList <Number> list = new ArrayList <Number>(); // 只能存放Number的子类,可以放整数,也可以放浮点数,但是不能放字符串

泛型的优点:可以在编译时检测出错误。泛型类或方法允许用户指定可以和这些类或方法一起工作的对象类型。如果试图使用一个不相容的对象,编译器就会检测出这个错误。

注意1
有时候,泛型类可能会有多个参数
在这种情况下,应将所有参数一起放在尖括号中,并用逗号分隔开,比如<E1,E2, E3>

注意2
可以定义一个类或者接口作为泛型类或者泛型接口的子类型。
例如,在Java API中,java.lang.String类被定义为实现Comparable接口,如下所示:
public class String implements Comparable<String>

注意3
为了创建一个字符串堆找,可以使用new GenericStack<String>() 或new GenericStack<>() 。
这可能会误导你认为GenericStack 的构造方法应该定义为 public GenericStack<E>()这是错误的。
它应该被定义为 public GenericStack()
 
 

3、设计泛型类

方法申明 public class Heros <E> {… your code…}

方法调用 Heros <Type> ADhero1 = new Name<>();
 

定义泛型类
以Stack栈为例子,如果不使用泛型
当需要一个只能放Hero的栈的时候,就需要设计一个HeroStack
当需要一个只能放Item的栈的时候,就需要一个ItemStack。

所以,此时就考虑用泛型类。
`在类的声明上,加上一个 <T>,表示该类支持泛型。

E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型`

public class GenericStack <E> {
	private java.util.ArrayList<E> list = new java.util.ArrayList<>();
	
	public int getSize() {
		return list.size();
	}
	public E peek() {
		return list.get(getSize()-1);
	}	
	public void push(E o) {
		list.add(o);
	}	
	public E pop() {
		E o = list.get(getSize()-1);
		list.remove(getSize()-1);
		return o;
	}
	public boolean isEmpty() {
		return list.isEmpty();
	}
	@Override
	public String toString() {
		return "stack:" + list.toString();
	}
}
public class MyStack<T> {
   
    LinkedList<T> values = new LinkedList<T>();
       
    public void push(T t) {
        values.addLast(t);
    }
   
    public T pull() {
        return values.removeLast();
    }
   
    public T peek() {
        return values.getLast();
    }

泛型类调用

public class Main {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		GenericStack <String> stack1 = new GenericStack<>();  // 上文的注意点3
		stack1.push("London1");
		stack1.push("London2");
		stack1.push("London3");
		
		GenericStack <Integer> stack2 = new GenericStack<>();
		stack2.push(1);
		stack2.push(2);
		stack2.push(3);
	}
}

定义、调用泛型方法

public class Main {
	public static void main(String[] args) {
	
		Integer[] integers = {1,2,3,4,5};
		String[] strings = {"aaa","bbb","Ccc"};
		Main.<Integer>print(integers);  //调用泛型方法
		Main.<String>print(strings);
		print(integers);   // 或者这样调用
	}
	
	public static <E> void print(E[] list) {  // 声明泛型方法,放在关键字static之后
		for(int i = 0;i < list.length; i++)
			System.out.print(list[i] + "  ");
		System.out.println();
	}
}

示例学习,用泛型方法进行排序

public class GenericSort {
	
	public static void show() {
		Double[] doubleArray = {new Double(2.5),new Double(6.4),new Double(3.3)};
		Character[] charArray = {new Character('a'),new Character('q'),new Character('c')};
		String[] stringArray = {"11","22","33"};
	
		sort(doubleArray);
		sort(charArray);
		System.out.print("sorted Double objects:  ");printList(doubleArray);
		System.out.print("sorted char objects:   "); printList(charArray);	
	}
	
	public static <E extends Comparable<E>> void sort(E[] list) {  //可以对任何对象类型的数组进行排序
		E currentMin;
		int currentMinIndex;
		
		for(int i = 0; i < list.length -1 ;i++) {
			currentMin = list[i];
			currentMinIndex = i;
			for (int j = i+1 ; j < list.length; j++) {
				if(currentMin.compareTo(list[j])>0) {
					currentMin = list[j];
					currentMinIndex = j;
				}
			}
			
			if(currentMinIndex != i) {
				list[currentMinIndex] = list[i];
				list[i] = currentMin;
			}
		}
	}
	
	public static void printList(Object[] list) {
		for(int i = 0; i< list.length ; i++)
			System.out.print(list[i]+" ");
		System.out.println();
	}
}

泛型类型定义为<E extends Comparable <E>>,这具有两个含义:

首先,它指定E 是Comparable 的子类型;其次,它还指定进行比较的元素是E 类型的。
 
 

4、泛型通配符

通配泛型类型有三种形式一一?? extends T 或者? super T ,其中T是泛型类型。

第一种 ?
称为 非受限通配 (unbounded wildcard) ,它和? extends Object 是一样的。表示任何一种对象类型。

public static void print(GenericStack <?> stack)
 
?的缺陷1:
既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型。
所以只能以Object的形式取出来

 

第二种 ? extends T
称为 受限通配 (bounded wildcard),表示T 或T 的一个子类型

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型
heroList 的泛型可能是Hero
heroList 的泛型可能是APHero
heroList 的泛型可能是ADHero
所以 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
 
但是,不能往里面放东西,因为:
放APHero就不满足<ADHero>
放ADHero又不满足<APHero>
 
public static double max(GenericStack <? extends Number> stack)
所以max(new GenericStack <Integer>()) 合法的
max(new GenericStack <Integer>()) 合法

 

第三种 ? super T
称为 下限通配 (Iower-bound wildcard) ,表示T 或T 的一个父类型

GenericStack stack1 = new GenericStack<>(); 一个字串栈
GenericStack stack2 = new GenericStack<>(); 一个对象栈
如果要调用下面的add(stack1,stack2)方法,stack2就应该申明为 <? super T>
ArrayList heroList<? super Hero> 表示这是一个Hero泛型或者其父类泛型
 
heroList的泛型可能是Hero
heroList的泛型可能是Object
可以往里面插入Hero以及Hero的子类
但是取出来有风险,因为不确定取出来是Hero还是Object

所以,
如果希望只取出,不插入,就使用? extends Hero
如果希望只插入,不取出,就使用? super Hero
如果希望,又能插入,又能取出,就不要用通配符?

 
举例,在Main.java中添加下列程序

	public static double max(GenericStack<? extends Number> stack) {  // 子类型
		double max = stack.pop().doubleValue();
		
		while(!stack.isEmpty()) {
			double value = stack.pop().doubleValue();
			if(value>max)
				max = value ;
		}
		return max;
	}
	
	public static <T> void add(GenericStack<T> stack1,
			GenericStack<? super T> stack2) {
		while(!stack1.isEmpty())
			stack2.push(stack1.pop());
	}

5、注意

1 不管实际的具体类型是什么,泛型类是被它的所有实例所共享的。假定按如下方式创建list1和list2:
ArrayList list1 = new ArrayList<>();
ArrayList list2 = new ArrayList<>();
尽管在编译时ArrayList和ArrayList是两种类型,但在运行时只有一个ArrayList类被加载到JVM中。

2 在静态上下文中不允许类的参数是泛型类型。由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例所共享的,因此,在静态方法、数据域或者初始化语句中,为类引用泛型类型参数是非法的。

3 异常类不能是泛型的

4 子类泛型不可以转换为父类泛型

5父类泛型不可以转换为子类泛型
 
 

6、举例:矩阵运算

首先创建一个泛型类,<E extends Number> 表示该泛型类型是Number的子类型。
接下来,申明3个抽象方法,因为不知道元素的确切类型前不能实现它。
后面写的所有方法都是非静态的,因为我们使用的是泛型类型E来表示类。


public abstract class GenericMatrix <E extends Number>{  //矩阵元素的类型是Number 的泛型子类
	
    protected abstract E add(E o1, E o2);		
    protected abstract E multiply(E o1, E o2); 	
    protected abstract E zero();				//零矩阵
 
   
    //矩阵的加法运算、乘法运算、结果显示
    public E[][] addMatrix(E[][] matrix1, E[][] matrix2) { 		//将两个矩阵相加,用泛型E来表示类,所以方法是非静态的;

        if (  (matrix1.length != matrix2.length) || (matrix1[0].length != matrix2[0].length)  )
            throw new RuntimeException("The matrices do not have the same size!");
       
        E[][] result = (E[][]) new Number[matrix1.length][matrix1[0].length];   //注意E[][] result = new Number[matrix.length][matrix1[0].length]是不对的,
        							//因为不能用泛型类型来创建数组,要用(E[][])来转换才能创建数组;
    
        for(int i = 0 ;i < result.length; i++)
        	for (int j = 0; j <result[i].length; j++) {
        		result [i][j] = add(matrix1[i][j],matrix2[i][j]);
        	}
        return result;
    }
    
    //multiply 
    public E[][] multiplyMatrix(E[][] matrix1, E[][] matrix2) {

        if (matrix1[0].length != matrix2.length)
            throw new RuntimeException("The matrices do not have compatible size!");
       
        E[][] result = (E[][]) new Number[matrix1.length][matrix2[0].length];
     
        for(int i = 0 ;i < result.length; i++) {
        	for (int j = 0; j <result[0].length; j++) {
        		result [i][j] = zero();
        		
        		for(int k= 0 ;k < matrix1[0].length; k++) {
        			result[i][j] = add(result[i][j],multiply(matrix1[i][k],matrix2[k][j]));
        		}
        	}
        }
        return result;
    }
    
    public static void printResult(Number[][] m1 ,Number[][] m2, Number[][] m3, char op) {
    	for(int i = 0; i<m1.length ; i++) {
    		for ( int j = 0; j < m1[0].length; j++)
    			System.out.print("  " + m1[i][j]);
    		
    		if (i == m1.length / 2)
    			System.out.print("  " + op + "  ");
    		else 
    			System.out.print("     ");
    		
    		for(int j = 0; j < m2.length; j++)
    			System.out.print("  " + m2[i][j]);
    		
    		if (i == m1.length / 2)
    			System.out.print("  =   ");
    		else 
    			System.out.print("      ");
    		
    		for(int j = 0; j < m3.length; j++)
    			System.out.print(m3[i][j] + "   ");
    		
    		System.out.println();    		
 	  	}
    }
       
}

 
接下来是对GenericMatrix的Integer示例的部分函数重载
该程序实现了Integer 对象的add 、multiply 和zero 方法。因为这些方法只能被addMatrix 和multiplyMatrix 方法调用,所以,它们仍然是protected 的。

public class IntegerMatrix extends GenericMatrix<Integer>{
	@Override
	protected Integer add(Integer o1,Integer o2) {
		return o1 + o2;
	}
	
	@Override
	protected Integer multiply(Integer o1,Integer o2) {
		return o1 * o2;
	}
	
	@Override
	protected Integer zero() {
		return 0;
	}
}

 
main函数

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Integer[][] m1 = new Integer[][] {{1,2,3},{4,5,6},{1,1,1}};
		Integer[][] m2 = new Integer[][] {{1,1,1},{2,2,2},{0,0,0}};
		
		IntegerMatrix integerMatrix = new IntegerMatrix();
		System.out.println("\n m1 + m2 is ");
		GenericMatrix.printResult(m1, m2, integerMatrix.addMatrix(m1, m2), '+');
		
		System.out.println("\n m1 * m2 is ");
		GenericMatrix.printResult(m1, m2, integerMatrix.multiplyMatrix(m1, m2), '*');
		}
}

显示结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/L20902/article/details/88878731