深入理解java泛型Generic

1. 背景

泛型技术诞生之前(JDK5以前),创建集合的类型都是Object 类型的元素,存储内容没有限制,编译时正常,运行时容易出现ClassCastException 异常。

public class Test {
	public static void main(String[] args) {
		ArrayList list = new ArrayList();
		list.add("java");
		list.add(100);
		list.add(true);
		for(int i = 0;i <list.size();i++) {
			Object o = list.get(i);
			String str = (String)o;
			System.out.println(str);
		}
	}
}

输出:

java
Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.chengyu.junit.Test.main(Test.java:18)

2. 泛型概念

JDK5 中引入泛型,从而可以在编译时检测是否存在非法的类型数据结构
其本质就是参数化类型,可以用于类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

public static void main(String[] args) {
		ArrayList<String> strList = new ArrayList<String>();
		strList.add("java");
		strList.add("C#");

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

3. 泛型类

3.1 定义与调用

定义类时设置泛型,该泛型可用于类中的属性或方法中,调用该泛型类时,指定具体类型;

// 调用泛型类
public class GenericTest {
	public static void main(String[] args) {
		Generic<String> strGen = new Generic<>("a");
		String key = strGen.getKey();
		System.out.println(key);
	}
}
// 定义泛型类
// @param <T> 使用类时指定
class Generic<T>{
	private T key;
	public Generic(T key) {
		this.key = key;
	}
	public T getKey() {
		return key;
	}
	public void setKey(T key) {
		this.key = key;
	}
	@Override
	public String toString() {
		return "GenericTest [key=" + key + "]";
	}
}

3.2 注意

1)调用泛型类时未定义类型,则会按照Object 类型处理;
2)调用时分别指定不同类型,但本质都是Object 类型;
3)泛型不支持基本数据类型;
4)泛型类的成员方法不可以用static 修饰(泛型方法可以)。

3.3 使用

需求:抽奖活动,但抽奖内容没有确定,可能都是现金,也可能都是物品

public class ProductGetterTest {
	public static void main(String[] args) {
		// 抽物品
		ProductGetter<String> strProductGetter = new ProductGetter<>();
		String[] str = {"手机","电视","洗衣机"};
		for(int i = 0; i < str.length; i ++ ) {
			strProductGetter.addProduct(str[i]);
		}
		String strProduct = strProductGetter.getProduct();
		System.out.println("恭喜您抽中了" + strProduct);

		System.out.println("=============================================");
		// 抽现金
		ProductGetter<Integer> intProductGetter = new ProductGetter<>();
		Integer[] integer = {1000,2000,3000};
		for(int i = 0; i < integer.length; i ++ ) {
			intProductGetter.addProduct(integer[i]);
		}
		int intProduct = intProductGetter.getProduct();
		System.out.println("恭喜您抽中了" + intProduct);
	}
}
// 抽奖器
class ProductGetter<T>{
	Random random = new Random();
	// 奖品池
	ArrayList<T> list = new ArrayList<>();
	// 添加奖品
	public void addProduct(T t) {
		list.add(t);
	}
	// 抽奖(获取奖品)
	public T getProduct() {
		 return list.get(random.nextInt(list.size()));
	}
}

3.4 泛型类的继承

3.4.1 子类也是泛型类

子类也是泛型类,则泛型要保持一致。

class ChildFirst<T> extends Parent{ ... }

1)指定子类泛型,不指定父类泛型,父类默认为Object 类型

class Parent<E>{
	private E value;
	public E getValue() {
		return value;
	}
	public void setValue(E value) {
		this.value = value;
	}
}
class ChildFirst<T> extends Parent{
	@Override
	public Object getValue() {
		return super.getValue();
	}
}

2)若父类保留原有泛型,与子类泛型不一致,则会编译出错

class ChildFirst<T> extends Parent<E>{
	@Override
	public E getValue() {
		return super.getValue();
	}

3)父类泛型与子类保持一致
具体泛型指定是由子类传递到父类当中,所以继承时父类要与子类泛型保持一致(当然都写成E也可以)。

class Parent<E>{
	private E value;
	public E getValue() {
		return value;
	}
	public void setValue(E value) {
		this.value = value;
	}
}
class ChildFirst<T> extends Parent<T>{
	@Override
	public T getValue() {
		return super.getValue();
	}
}

4)调用

public class GenericTest {
	public static void main(String[] args) {
		ChildFirst<String> childFirst = new ChildFirst<>();
		childFirst.setValue("chengyu");
		System.out.println(childFirst.getValue());
	}
}

5)补充:
子类可以进行泛型扩展,但子类必须有一个泛型与父类一致

public class GenericTest {
	public static void main(String[] args) {
		ChildFirst<String,Integer> childFirst = new ChildFirst<>();
		childFirst.setValue("chengyu");
		System.out.println(childFirst.getValue());
	}
}
class Parent<E>{
	private E value;
	public E getValue() {
		return value;
	}
	public void setValue(E value) {
		this.value = value;
	}
}
class ChildFirst<T,E> extends Parent<T>{
	@Override
	public T getValue() {
		return super.getValue();
	}
}

3.4.2 子类不是泛型类

子类不是泛型类,父类要明确泛型的数据类型

class ChildSecond extends Parent<String>{ ... }

1)子类不是泛型类,不指定父类泛型,父类默认为Object 类型

class Parent<E>{
	private E value;
	public E getValue() {
		return value;
	}
	public void setValue(E value) {
		this.value = value;
	}
}
class ChildSecond extends Parent{
	@Override
	public Object getValue() {
		return super.getValue();
	}
}

2)父类要明确泛型的数据类型

class ChildSecond extends Parent<String>{
	@Override
	public String getValue() {
		return super.getValue();
	}
}

3)调用

public class GenericTest {
	public static void main(String[] args) {
		ChildSecond childSecond = new ChildSecond();
		childSecond.setValue("chengyu2");
		System.out.println(childSecond.getValue());
	}
}

4. 泛型接口

4.1 定义

public interface Generator<T>{ ... }

4.2 使用(与继承特点相同)

4.2.1 实现类不是泛型类

实现类不是泛型类,接口要明确数据类型

class Apple implements Generator<String>{
	@Override
	public String getKey() {
		return "Generator interface";
	}
}

4.2.2 实现类也是泛型类

实现类也是泛型类,实现类和接口的泛型类型要一致

class Apple<T> implements Generator<T>{
	private T key;
	@Override
	public T getKey() {
		return key;
	}
}

5. 泛型方法

5.1 定义

5.1.1 泛型方法

修饰符 <T,E,..> 返回值类型 方法名(形参列表){
}

// 泛型方法
public <E,T> E getProduct(ArrayList<E> list) {
	return list.get(random.nextInt(list.size()));
}

5.1.2 静态泛型方法

修饰符 <T,E,..> 返回值类型 方法名(形参列表){
}

// 泛型方法
public <E,T> E getProduct(ArrayList<E> list) {
	return list.get(random.nextInt(list.size()));
}

5.1.3 可变参数的泛型方法

public <E> void print(E... e) {
	for(int i = 0; i < e.length;i++){
		System.out.println(e[i]);
	}
}
// 调用
print(1,2,3,4);

5.2 注意

1)包含泛型列表的方法才是泛型方法,泛型类中使用了泛型的方法并不是泛型方法;
2)泛型列表中声明了泛型类型,才可以在方法中使用泛型类型;
3)泛型方法中的泛型类型独立于泛型类的泛型,与类泛型类型无关;
4)泛型方法可以用static 修饰(泛型类的成员方法不可以)。

5.3 使用

5.3.1 定义泛型方法

// 抽奖器
// @param <t>
class ProductGetter<T>{
	Random random = new Random();
	// 奖品池
	ArrayList<T> list = new ArrayList<>();
	// 添加奖品
	public void addProduct(T t) {
		list.add(t);
	}
	// 抽奖(获取奖品)
	public T getProduct() {
		 return list.get(random.nextInt(list.size()));
	}
	// 泛型方法
	public <E> E getProduct(ArrayList<E> list) {
		return list.get(random.nextInt(list.size()));
	}
}

5.3.2 调用泛型方法

public class ProductGetterTest {
	public static void main(String[] args) {
		ProductGetter<Integer> intProductGetter = new ProductGetter<>();

		ArrayList<String> strList = new ArrayList<>();
		strList.add("手机");
		strList.add("电视");
		strList.add("洗衣机");

		String product = intProductGetter.getProduct(strList);
		System.out.println("恭喜您抽中了" + product);
	}
}

6. 类型通配符

6.1 类型通配符介绍

类型通配符一般用【?】代替具体的类型 实参

6.2 为什么要用类型通配符

泛型类被调用时,需要指定泛型类型,当泛型类的方法被调用时,不能灵活对应多种泛型类型的需求,如下面代码【4.】部分所示:

public class BoxTest {
	public static void main(String[] args) {
		// 3.调用showBox
		Box<Number> box1 = new Box<>();
		box1.setFirst(100);
		showBox(box1);
		// 4.再次调用showBox
		// 出现问题:类型发生变化后会报错
		Box<Integer> box2 = new Box<>();
		box2.setFirst(200);
		showBox(box2);
	}
	// 2. 调用泛型类,此时需要指定泛型类型
	public static void showBox(Box<Number> box) {
		Number first = box.getFirst();
		System.out.println(first);
	}
}
// 1.定义泛型类
class Box<E>{
	private E first;
	public E getFirst() {
		return first;
	}
	public void setFirst(E first) {
		this.first = first;
	}
}

解决上述问题,类型通配符【?】登场

public class BoxTest {
	public static void main(String[] args) {
		// 3.调用showBox
		Box<Number> box1 = new Box<>();
		box1.setFirst(100);
		showBox(box1);
		// 4.再次调用showBox
		// 问题得以解决
		Box<Integer> box2 = new Box<>();
		box2.setFirst(200);
		showBox(box2);
	}
	// 2. 调用泛型类,此时需要指定泛型类型
	// 【?】类型通配符等成
	public static void showBox(Box<?> box) {
		Object first = box.getFirst();
		System.out.println(first);
	}
}
// 1.定义泛型类
class Box<E>{
	private E first;
	public E getFirst() {
		return first;
	}
	public void setFirst(E first) {
		this.first = first;
	}
}

6.3 泛型通配符上限 extends

【6.2】代码例中,虽然使用了通配符,但 box.getFirst()返回类型仍然需要定义成Object 类型,并不理想。

public static void showBox(Box<?> box) {
	Object first = box.getFirst();
	System.out.println(first);
}

通配符上限登场:
调用showBox 方法时,传递的泛型类型可以是Number 及其子类(Integer)

public static void showBox(Box<? extends Number> box) {
	Number first = box.getFirst();
	System.out.println(first);
}

注意:

public static void showBox(Box<? extends Number> box) {
	Number first = box.getFirst();
	// 此处编译报错:类型不一致
	// 虽然定义上限,showBox 被调用时没问题,但在方法内同时定义多种类型,编译器无法识别
	Integer second = box.getFirst();
	System.out.println(first);
}

6.4 泛型通配符下限 super

public static void showBox(Box<? super Integer> box) {
	Number first = box.getFirst();
	System.out.println(first);
}

注意:
遍历时要用Object 类型进行遍历;

7. 类型擦除

泛型信息只存在于代码编译阶段,进入JVM之前,与泛型相关的信息会被擦除,这个行为称为类型擦除。

到此这篇关于深入理解java泛型Generic的文章就介绍到这了,更多相关java泛型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 初探Java中的泛型

    泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解: 1.什么是泛型. 2.如何使用泛型. 3.泛型的好处. 1.什么是泛型? 泛型,字面意思便是参数化类型,平时所面对的类型一般都是具体的类型,如果String,Integer,Double,而泛型则是把所操作的数据类型当作一个参数.如,ArrayList<String>(),通过传入不同的类型来指定容器中存储的类型,而不用为不同的类型创建不同的类,这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类

  • Java 泛型全解析

    泛型简介 什么是泛型? 参化类型,数是JDK1.5的新特性.(定义泛型时使用参数可以简单理解为形参),例如List<E>,Map<K,V> 编译时的一种类型,此类型仅仅在编译阶段有效,运行时无效.例如List<String>在运行时String会被擦除,最终系统会认为都是Object. 为什么要使用泛型? 泛型是进行类型设计或方法定义时的一种约束规范,基于此规范可以: 提高编程时灵活性(有点抽象,后续结合实例理解). 提高程序运行时的性能.(在编译阶段解决一些运行时需要

  • Java集合遍历实现方法及泛型通配

    集合定义 集合,集合是java中提供的一种容器,可以用来存储多个数据. 特点:数组的长度是固定的.集合的长度是可变的.集合中存储的元素必须是引用类型数据' 普通for遍历: //案例一 ArrayList<Person> arr=new ArrayList<Person>(); arr.add(new Person("张三",19)); arr.add(new Person("小红帽",20)); arr.add(new Person(&qu

  • 详解Java 中泛型的实现原理

    泛型是 Java 开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码.本文总结了 Java 泛型的三种形式以及泛型实现原理. 泛型 泛型的本质是对类型进行参数化,在代码逻辑不关注具体的数据类型时使用.例如:实现一个通用的排序算法,此时关注的是算法本身,而非排序的对象的类型. 泛型方法 如下定义了一个泛型方法, 声明了一个类型变量,它可以应用于参数,返回值,和方法内的代码逻辑. class GenericMethod{ public <T> T[] sort(T[]

  • 详细分析Java 泛型的使用

    一.泛型的简介 1.为什么要使用泛型? 一般使用在集合上,比如现在把一个字符串类型的值放入到集合里面,这个时候,这个值放到集合之后,失去本身的类型,只能是object类型.这时,如果想要对这个值进行类型转换,很容易出现类型转换错误,怎么解决这个问题,可以使用泛型来解决. 2.在泛型里面写是一个对象,String 不能写基本的数据类型 比如int,要写基本的数据类型对应的包装类 基本数据类型 对应包装类 基本数据类型 对应包装类 byte Byte short Short int Integer

  • 深入了解JAVA泛型

    什么是泛型 泛型的概念:Java泛型(generics)是JDK1.5中引入的一个新特性,泛型提供了编译时的类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构. 泛型的本质就是类型参数化,也就是所操作的数据类型被指定为一个参数. 使用泛型的好处: 1  在编译期间提供了类型检查 2  取数据时无须进行类型装换 泛型类.接口 泛型类 语法: class 类名称 <泛型标识,泛型标识,泛型标识,...> { private 泛型标识 变量名: // ... } 常用的泛型标识:T.E

  • Java让泛型实例化的方法

    泛型对象可以实例化吗? 不可以,T t=new T()是不可以的,编译器会报错.由于泛型擦除,编译器在编译时无法确定泛型所对应的真实类型 解决方法 使用反射新建实例 Type superclass = getClass().getGenericSuperclass(); ParameterizedType parameterizedType = null; if (superclass instanceof ParameterizedType) { parameterizedType = (Pa

  • Java静态泛型使用方法实例解析

    前言:当工具类对多个模型类进行排序,比较等操作的时候,需要书写大量重复代码,因为懒人总要想怎么省事的,所以考虑使用泛型这个玩意简化代码 案例:当前存在两个模型类,Fruit和Person,他们都需要排序方法而且业务逻辑各不相同,因此需要分别写两个排序方法,但因为排序相同的地方太多,唯一的区别就是判断两个对象的大小关系,于是在此做简化操作. 执行步骤: 1.编写模型类接口 interface Model public interface Model<T> { public int compare

  • Java封装数组之改进为泛型数组操作详解

    本文实例讲述了Java封装数组之改进为泛型数组操作.分享给大家供大家参考,具体如下: 前言:通过上一节我们对我们需要封装的数组,进行了基本的增删改查的封装,但只局限于int类型的操作,为了能提供多种类型数组的操作,我们可以将其进一步封装为泛型数组. 1.定义泛型数组相关概念 (1)泛型数组让我们可以存放任何数据类型 (2)存放的类型不可以是基本数据类型,只能是类对象 基本类型: boolean.byte.char.short.int.long.float.double (3)每个基本数据类型都有

  • java基础之泛型知识点总结

    一.什么是泛型?为什么要使用泛型? 泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参). 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型).也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数

  • Java泛型变量如何添加约束

    有时,类或方法需要对类型变量加以约束.下面是一个典型的例子,我们要寻找数组中的最小元素: public class ArrayAlg { public static <T extends Comparable> T min(T[] array){ if (array == null || array.length == 0){ return null; } T smallest = array[0]; for (int i=0;i<array.length;i++){ if (small

  • Java反射,泛型在Json中的运用

    最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的框架中也能看到它的身影,可以在运行时检查类,接口.变量和方法等信息,可以实例化调用方法以及设置变量值等.本文主要以代码的形式直接将反射,泛型的运用展现出来. java中的反射 首先新建一个基础类Author. package bean; /** * * @author Super~me * Desc

  • 深入分析JAVA 反射和泛型

    从 JDK5 以后,Java 的 Class 类增加了泛型功能,从而允许使用泛型来限制 Class 类,例如,String.class 的类型实际上是 Class<String>.如果 Class 对应的类暂时未知,则使用 Class<?>.通过在反射中使用泛型,可以避兔使用反射生成的对象需要强制类型转换. 泛型和 Class 类 使用 Class<T> 泛型可以避免强制类型转换.例如,下面提供一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例. public

随机推荐