Java中泛型的使用

1. 概述

在前面学习集合时,我们知道了集合中是可以存放任意对象的,只要把对象存储到集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作时,必须采用类型转换。比如下面程序:

public static void main(String[] args) {
   
		List list = new ArrayList();
		list.add("abc");
		list.add("def");
		list.add(123);// 由于集合没有做任何限定,任何类型都可以给其中存放
		// 相当于:Object obj=new Integer(5);

		Iterator it = list.iterator();
		while (it.hasNext()) {
   
			// 需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
			String str = (String) it.next();// String str=(String)obj;
			// 编译时期仅检查语法错误,String是Object的儿子可以向下转型
			// 运行时期String str=(String)(new Integer(5)),
			// 由于String与Integer没有父子关系所以转换失败。
			// 程序在运行时发生了问题java.lang.ClassCastException
			System.out.println(str.length());
		}
}

可以发现:
这种数据类型间的强制转换,存在安全隐患,所以为了解决这个安全问题,Java中引入了泛型技术。

2. 定义

泛型就是指明集合中存储数据的类型。如果没有指明数据类型,那集合可以存储任意类型,就会存在上述提到的类型转换异常的安全隐患。

3. 使用

在集合类型后面写上尖括号<>,并加上要存储的数据类型。如:ArrayList<Integer> arr = new ArrayList<Integer>();
示例:

public static void function() {
   
		Collection<String> coll = new ArrayList<String>();//指明集合中存储的数据类型为String
		coll.add("abc");
		coll.add("qwe");
		//coll.add(123);
		Iterator<String> it = coll.iterator();//集合指明为String,所以迭代器取出来的元素也必须为String
		while (it.hasNext()) {
   
			String s = it.next();//此处便不需要数据类型强转了
			System.out.println(s.length());
		}
}

使用泛型后,不用做数据类型的转换,一定程度上精简了代码;并且如果添加的元素不符合所指明的数据类型,代码将会标红报错,无法编译,提高了安全性。

4. 补充

java中的泛型,其实是一种伪泛型,它只是一种编译手段,如:

ArrayList<String> arr; 
arr.add("");

如果add中的对象是String则编译成功,否则编译失败。但是,在编译后的class文件中,是没有泛型的。所以java中的泛型是假的,编译后的class文件中没有泛型概念。也就是说如果Java文件反编译回源文件是没有泛型的。(好像C++中的泛型是真的 ^ o ^ )

5. 带有泛型的类、方法和接口

上文中我们在集合中大量使用到了泛型,但泛型同样可灵活地将数据类型应用到不同的类、方法、接口中。将数据类型作为参数进行传递。

  • 泛型的类语法修饰符 class 类名 <E> { }。E:Element 元素,实际思想就是一个变量而已。如:ArrayList<Integer>,E接受到的类型就是Integer类型。

  • 泛型的方法语法修饰符 <E> 返回值类型 方法名(参数){ }。例如,API中的ArrayList集合中的方法:

    public <E> E[] toArray(E[] a){ }
    
  • 泛型的接口语法修饰符 interface 接口名<E>{ }。如,

    public interface List <E>{
    	abstract boolean add(E e);
    }
    

总之在使用时直接将E视为一个未知或任意的数据类型就行了,你传的什么类型,E就变为那个类型。

6. 泛型的通配符

直接看示例代码,虽然还没讲到HashSet,但其公共用法都差不多,这里也不需要纠结于其用法,只是为了代码演示。

public class GenericDemo {
   
	public static void main(String[] args) {
   
		ArrayList<String> array = new ArrayList<String>();//存储String类型
		
		HashSet<Integer> set = new HashSet<Integer>();//存储Integer类型
		
		array.add("123");
		array.add("456");
		
		set.add(789);
		set.add(890);
		
	/* * 这时需要定义一个方法,要求可以同时迭代这2个集合,怎么办呢? 
	* 这时会发现 , 数据类型不确定,可能是String的,也可能是Integer的,参数既不能写ArrayList,也不能写HashSet。 
	* 那么有没有一种共同实现的接口呢? 
	* 这里需要用到泛型的 通配 ,作用是:匹配所有的数据类型。符号是: ? 
	*/
		iterator(array);
		iterator(set);
	}
	
	public static void iterator(Collection<?> coll){
   
		Iterator<?> it = coll.iterator();
		while(it.hasNext()){
   
			//it.next()获取的对象,任意类型。
			System.out.println(it.next());
		}
	}
}

7. 泛型的限定

同样直接看示例代码:(要求定义一个方法,可以同时遍历3集合,遍历三个集合的同时,还可以调用对象的方法)

酒店的员工类:

/* * 酒店的员工类 * 员工共性, 姓名,工号 工作方法 */
public abstract class Employee {
   
	private String name;
	private String id;
	
	public Employee(){
   }
	
	public Employee(String name,String id){
   
		this.name = name;
		this.id = id;
	}
	
	public abstract void work();
	
	public String getName() {
   
		return name;
	}
	public void setName(String name) {
   
		this.name = name;
	}
	public String getId() {
   
		return id;
	}
	public void setId(String id) {
   
		this.id = id;
	}
	
}

VIP接口:

/* * 酒店的VIP服务 * 厨师和服务员 */
public interface VIP {
   
	public abstract void services();
}

厨师类:

/* * 定义厨师类 * 属于员工一种,具有额外服务功能 * 继承Employee,实现VIP接口 */
public class ChuShi extends Employee implements VIP{
   
	//空参数构造方法
	public ChuShi(){
   }
	
	//有参数构造方法
	public ChuShi(String name,String id){
   
		super(name,id);
	}
	
	//抽象方法重写
	public void work(){
   
		System.out.println("厨师在炒菜");
	}
	public void services(){
   
		System.out.println("厨师做菜加量");
	}
}

服务员类:

/* * 定义服务员类 * 属于员工一种,具有额外服务功能 * 继承Employee,实现VIP接口 */
public class FuWuYuan extends Employee implements VIP{
   
	//空参数构造方法
	public FuWuYuan() {
   
		super();
		
	}
   //满参数构造方法
	public FuWuYuan(String name, String id) {
   
		super(name, id);
		
	}
	
	//抽象方法重写
	public void work(){
   
		System.out.println("服务员在上菜");
	}

	public void services(){
   
		System.out.println("服务员给顾客倒酒");
	}
}

经理类:

/* * 定义经理类 * 属于员工一种,没有VIP功能 * 自己有奖金属性 */
public class JingLi extends Employee {
   
	//空参数构造方法
	public JingLi(){
   }
	
	//满参数构造方法
	public JingLi(String name,String id,double money){
   
		super(name, id);
		this.money = money;
	}
	
	//定义奖金属性
	private double money;
	
	public double getMoney() {
   
		return money;
	}
	
	public void setMoney(double money) {
   
		this.money = money;
	}

	//重写抽象方法
	public void work(){
   
		System.out.println("管理,谁出错我罚谁");
	}
}

运行主程序类:

/* * 将的酒店员工,厨师,服务员,经理,分别存储到3个集合中 * 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 */
import java.util.ArrayList;
import java.util.Iterator;
public class GenericTest {
   
	public static void main(String[] args) {
   
		//创建3个集合对象
		ArrayList<ChuShi> cs = new ArrayList<ChuShi>();
		ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>();
		ArrayList<JingLi> jl = new ArrayList<JingLi>();
		
		//每个集合存储自己的元素
		cs.add(new ChuShi("张三", "后厨001"));
		cs.add(new ChuShi("李四", "后厨002"));
		
		fwy.add(new FuWuYuan("翠花", "服务部001"));
		fwy.add(new FuWuYuan("酸菜", "服务部002"));
		
		jl.add(new JingLi("小名", "董事会001", 123456789.32));
		jl.add(new JingLi("小强", "董事会002", 123456789.33));
		
	/* * 要求定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work 
	* ? 通配符,迭代器it.next()方法取出来的是Object类型,无法调用work方法 
	* 方法一:强制转换: it.next()=Object o ==> Employee 
	* 方法二:参数控制,可以传递Employee对象,也可以传递Employee的子类的对象, 
	* 这就要用到泛型的限定 本案例中,父类固定Employee,但是子类可以为任意。 
	* 语法为: < ? extends Employee > 限制的是父类, 上限限定, 可以传递Employee,也可以传递它的子类对象。 
	*/
		iterator(jl);
		iterator(fwy);
		iterator(cs);
	}
	
	public static void iterator(ArrayList<? extends Employee> array){
   
		
		 Iterator<? extends Employee> it = array.iterator();
		 while(it.hasNext()){
   
			 //获取出的next() 数据类型,可以是任意Employee类型
			 Employee e = it.next();
			 e.work();
		 }
	}
}

另外,< ? super Employee >限制的是子类, 下限限定, 可以传递Employee和它的父类对象。注意与< ? extends Employee >区别。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java泛型主要用于集合类、类和接口的定义,以及方法的参数和返回类型。下面是一些泛型的具体使用方式: 1. 定义泛型类:可以在类名后面使用尖括号 `<T>` 来表示该类是一个泛型类,其的 `T` 代表类型参数。例如:`public class MyGenericClass<T> { ... }` 2. 定义泛型接口:与泛型类类似,可以在接口名后面使用尖括号 `<T>` 来表示该接口是一个泛型接口。例如:`public interface MyGenericInterface<T> { ... }` 3. 使用泛型类型参数:在类或接口内部,可以使用泛型类型参数 `T` 来声明成员变量、方法参数、返回类型等。例如:`private T myVariable;`、`public void myMethod(T parameter) { ... }`、`public T myMethod() { ... }` 4. 泛型通配符:可以使用 `?` 表示任意类型的通配符。例如:`List<?> myList;` 表示一个未知类型的列表。 5. 限定泛型类型参数:可以使用 extends 关键字来限定泛型类型参数的上界。例如:`public class MyGenericClass<T extends Number> { ... }`,表示 `T` 必须是 Number 类或其子类。 6. 泛型方法:可以在方法的返回类型前面使用 `<T>` 来定义泛型方法。例如:`public <T> void myMethod(T parameter) { ... }`,其的 `<T>` 表示该方法是一个泛型方法。 通过使用泛型,可以在编译时进行类型检查,避免了在运行时出现类型转换错误。同时,泛型还能提高代码的可读性和安全性,使代码更加灵活和通用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值