文章目录

  • 1.泛型概念的提出
  • 2.泛型集合
  • 3.泛型方法
  • 4.泛型类
  • 5.泛型接口
  • 6.泛型通配符 ?
  • 7.总结


1.泛型概念的提出

泛型,即“参数化类型”。Java5开始出现的一种对Java语 言类型的一种拓展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使 用参数类型时指定的类型占位符,就好比方法的形式参数是实际参数的占位符一样.

  • Tiger类
public class Tiger {
	
	String name;

	public Tiger() {
	}

	public Tiger(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Tiger@name:" + this.name;
	}

	public void eat() {
		System.out.println(this.name + "吃羊");
	}
}
  • Sheep类
class Sheep {
	String name;

	public Sheep() {
	}

	public Sheep(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Sheep@name:" + this.name;
	}

	public void eat() {
		System.out.println(this.name + "吃青草");
	}
}
  • 测试类
public class Test {

	public static void main(String[] args) {
		ArrayList arr = new ArrayList();
		arr.add(new Tiger("华南虎"));
		arr.add(new Tiger("东北虎"));
		arr.add(new Sheep("喜羊羊"));
		System.out.println(arr);
		Iterator it = arr.iterator();
		while (it.hasNext()) {
			Object next = it.next();
			Tiger t = (Tiger) next;
			t.eat();
		}
	}
}

原因 :发现虽然集合可以存储任意对象,但是如果需要使用对象的特有方法,那么就需要类型转换,如果集合中存入的对象不同,可能引发类型转换异常.

  • 出现问题

存入的是特定的对象,取出的时候是Object对象,需要强制类型转换,可能诱发类型转换异常.无法控制存入的是什么类型的对象,取出对象的时候进行强转时可能诱发异常.而且在编译时期无法发现问题.

虽然可以再类型转换的时候通过if语句进行类型检查(instanceof),但是效率较低.(例如吃饭的时候,还需要判断米饭里有没有沙子,吃饭效率低).可以通过给容器加限定的形式规定容器只能存储一种类型的对象.就像给容器贴标签说明该容器中只能存储什么样类型的对象。

所以在jdk5.0后出现了泛型!

泛型的基本术语,以ArrayList<E>为例:<>念着typeof ArrayList<E>中的E称为类型参数变量

ArrayList<Integer>中的Integer称为实际类型参数。整个称为ArrayList<E>泛型类型。整个ArrayList<Integer>称为参数化的类型ParameterizedType

2.泛型集合

JavaSE(二十二)-泛型_Java知识点

  • 格式
集合类<类类型>  变量名  = new  集合类<类类型>();
public static void main(String[] args) {
	// 使用泛型后,规定该集合只能放羊,老虎就进不来了.
	ArrayList<Sheep> arr = new ArrayList<Sheep>();
	arr.add(new Sheep("美羊羊"));
	arr.add(new Sheep("懒洋洋"));
	arr.add(new Sheep("喜羊羊"));
	// 编译失败
	// arr.add(new Tiger("东北虎"));
	System.out.println(arr);
	Iterator<Sheep> it = arr.iterator();
	while (it.hasNext()) {
		// 使用泛型后,不需要强制类型转换了
		Sheep next = it.next();
		next.eat();
	}
}
  1. 将运行时的异常提前至编译时发生。
  2. 获取元素的时候无需强转类型,就避免了类型转换的异常问题
  • 格式 通过<> 来指定容器中元素的类型.
  • 什么时候使用泛型: 当类中操作的引用数据类型不确定的时候,就可以使用泛型类.
  • JDK5.0之前的Comparable
package java.lang;

public interface Comparable {
    public int compareTo(Object o);
}
  • JDK5.0之后的Comparable
package java.lang;

public interface Comparable<T> {
    public int compareTo(T o);
}

这里的<T>表示泛型类型,随后可以传入具体的类型来替换它.

  • 细节一: 声明好泛型类型之后,集合中只能存放特定类型元素
public static void main(String[] args) {
	// 创建一个存储字符串的list
	ArrayList<String> arr = new ArrayList<String>();
	arr.add("tom");
	arr.add("jack");
	// 存储非字符串编译报错.
	arr.add(1);
}
  • 细节二: 泛型类型必须是引用类型
public static void main(String[] args) {
	// 泛型类型必须是引用类型,也就是说集合不能存储基本数据类型
	// ArrayList<int> arr2=new ArrayList<int>();
	// 使用基本数据类型的包装类
	ArrayList<Integer> arr2 = new ArrayList<Integer>();
}
  • 细节三: 使用泛型后取出元素不需要类型转换.
public static void main(String[] args) {
	ArrayList<String> arr = new ArrayList<String>();
	arr.add("北京");
	arr.add("上海");
	arr.add("广州");
	// 使用泛型后取出元素不需要类型转换.
	String str = arr.get(0);
	System.out.println();
}

3.泛型方法

JavaSE(二十二)-泛型_Java基础_02

  • 函数上的泛型定义。当函数中使用了一个不明确的数据类型,那么在函数上就可以进行泛型的定义。
public <泛型的声明> 返回值类型  函数名( 泛型 变量名  ){
 
 
 }
public class GenericMethodTest {

	// 泛型方法 printArray
	public static <E> void printArray(E[] inputArray) {
		// 输出数组元素
		for (E element : inputArray) {
			System.out.println(element);
		}
		System.out.println();
	}

	public static void main(String args[]) {
		// 创建不同类型数组: Integer, Double 和 Character
		Integer[] intArray = { 1, 2, 3, 4, 5 };
		Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
		Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

		System.out.println("整型数组元素为:");
		printArray(intArray); // 传递一个整型数组

		System.out.println("\n双精度型数组元素为:");
		printArray(doubleArray); // 传递一个双精度型数组

		System.out.println("\n字符型数组元素为:");
		printArray(charArray); // 传递一个字符型数组
	}
}
  • 有界的类型参数

可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。

要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界

  • 实例
    该例子中的泛型方法返回三个可比较对象的最大值。
public class MaximumTest {

	// 比较三个值并返回最大值
	public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
		T max = x; // 假设x是初始最大值
		if (y.compareTo(max) > 0) {
			max = y; // y 更大
		}
		if (z.compareTo(max) > 0) {
			max = z; // 现在 z 更大
		}
		return max; // 返回最大对象
	}

	public static void main(String args[]) {
		System.out.println("最大的数为是:"+maximum(3, 4, 5));
		System.out.println("最大的数为是:"+maximum(6.6, 8.8, 7.7));
		System.out.println("最大的数为是:"+maximum("pear", "apple", "orange"));
	}
}

4.泛型类

JavaSE(二十二)-泛型_Java核心技术_03

  • 格式
类上的泛型声明
	修饰符 class 类名<泛型>{	  
}
  • 实例
public class Demo1 {
	public static void main(String[] args) {
		Father<String> f = new Father<String>("jack");
		System.out.println(f.getT());
		
		Father<Integer> f2 = new Father<Integer>(20);
		System.out.println(f2.getT());
	}
}

class Father<T> {
	private T t;

	public Father() {
	}

	public Father(T t) {
		super();
		this.t = t;
	}

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}
}

如果Father类有子类,子类该如何实现

public class Demo1 {
	public static void main(String[] args) {
		Father<String> f = new Father<String>("jack");
		System.out.println(f.getT());

		Father<Integer> f2 = new Father<Integer>(20);
		System.out.println(f2.getT());
	}
}

class Father<T> {
	private T t;

	public Father() {
	}

	public Father(T t) {
		super();
		this.t = t;
	}

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}
}

// 子类指定了具体的类型
class Son extends Father<String> {
}

// 子类也需要使用泛型
class Son3<T> extends Father<T> {
}

// 错误写法,父类上定义有泛型需要进行处理
class Son2 extends Father<T> {
}

注意:静态方法不可以使用类中定义的泛型

因为类中的泛型需要在对象初始化时指定具体的类型,而静态优先于对象存在。那么类中的静态方法就需要单独进行泛型声明,声明泛型一定要写在static后,返回值类型之前

public class Demo6<T> {

	public static void main(String[] args) {
		System.out.println(new Demo6<Integer>().getData(100));
	}

	public T getData(T data) {
		return data;
	}

	// 静态方法
	public static T getData2(T data) {
		return data;
	}

//泛型类中 静态方法   类方法  类属性
	public static <T> T getData2(T data) {
		return data;
	}
}

5.泛型接口

JavaSE(二十二)-泛型_Java编程_04

public class Demo8 {
	public static void main(String[] args) {
		
		MyInter<Integer> my = new MyInter<Integer>();
		my.print(200);
		
		MyInter2 my2 = new MyInter2();
		my2.print("只能传字符串");
	}
}

interface Inter<T> {
	void print(T t);
}

// 实现不知为何类型时可以这样定义
class MyInter<T> implements Inter<T> {
	public void print(T t) {
		System.out.println("myprint:" + t);
	}
}

// 使用接口时明确具体类型。
class MyInter2 implements Inter<String> {
	@Override
	public void print(String t) {
		System.out.println("myprint:" + t);
	}
}

6.泛型通配符 ?

JavaSE(二十二)-泛型_Java知识点_05

public class Demo9 {

	public static void main(String[] args) {
		ArrayList<Object> arr = new ArrayList<Object>();
		arr.add(new Tiger("阿虎"));
		arr.add("你好");
		print(arr);

		// 将集合的泛型设置类String类型,是Object子类
		HashSet<String> hs = new HashSet<String>();
		hs.add("hello");
		hs.add("jack");
		// 由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过
		print(hs);
	}

	public static void print(Collection<Object> coll) {
		Iterator<Object> it = coll.iterator();
		while (it.hasNext()) {
			Object next = it.next();
			System.out.println(next);
		}
	}
}

但是,由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过该问题如何解决?

可以把方法的形参的泛型去掉,那么方法中就把集合中的元素当做Object类型处理.
也可以使用使用泛型通配符

public static void main(String[] args) {
	ArrayList<Object> arr = new ArrayList<Object>();
	arr.add(new Tiger("阿虎"));
	arr.add("你好");
	print(arr);

	// 将集合的泛型设置类String类型,是Object子类
	HashSet<String> hs = new HashSet<String>();
	hs.add("hello");
	hs.add("jack");
	// 由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过
	print(hs);
}

public static void print(Collection<?> coll) {
	Iterator<?> it = coll.iterator();
	while (it.hasNext()) {
		Object next = it.next();
		System.out.println(next);
	}
}

上述就使用了泛型通配符

通配符:?
public void show(List<?> list)
{

}
可以对类型进行限定范围。
?extends E: 接收E类型或者E的子类型。

? super E: 接收E类型或者E的父类型。

限定泛型通配符的边界

  • 限定通配符的上边界:extends
public class Demo10 {

    public static void main(String[] args) {
        firstPrint();
        secondPrint();
    }

    public static void print(List<? extends Number> list) {// 通配符作形参
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));// 使用get方法
        }
    }

    public static void firstPrint() {
        List<Float> list1 = new ArrayList<>();//使用 Float作实参类型
        list1.add(12.3f);
        list1.add(23.4f);
        print(list1);
    }

    public static void secondPrint() {

        List<Integer> list2 = new ArrayList<>();//使用Integer作实参类型
        list2.add(1);
        list2.add(2);
        list2.add(3);
        print(list2);
    }

    public static void thirdPrint() {

        List<String> list = new ArrayList<>();//使用Integer作实参类型
        list.add("aa");
        list.add("bb");
        list.add("cc");
        print(list);
    }
}
  • 限定通配符的下边界 super
public class Demo11 {

    public static void firstInflate(List<? super Integer> list) {// 装填整数
        list.add(1);
        list.add(2);
        list.add(3);
    }
    public static void secondInflate(List<? super Float> list) {// 装填浮点数
        list.add(1.1f);
        list.add(2.2f);
        list.add(1.3f);
    }

    public static void main(String[] args) {

        List<In> list = new ArrayList<>();

        firstInflate(list);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        System.out.println("------------------------------");
        list.clear();

        secondInflate(list);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

7.总结

JDK5中的泛型允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定
如果有泛型,那么在编译的时候就可以把异常显示出来!

注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。