疯狂Java讲义-泛型

泛型

本章思维导图

泛型

泛型入门

Java集合有个缺点——把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)。

编译时不检查类型的异常

下面代码将会看到编译时不检查类型所导致的异常。

import java.util.ArrayList;
import java.util.List;

public class ListErr {
   

	public static void main(String[] args) {
   
		// 创建一个只想保存字符串的List集合
		List strList = new ArrayList();
		strList.add("十年寒窗无人问");
		strList.add("纵使相逢应不识");
		// "不小心"把一个Integer对象"丢进"了集合
		strList.add(5);
		strList.forEach(str -> System.out.println(((String) str).length()));
	}

}

上面程序创建了一个List集合,而且只希望该List集合保存字符串对象——但程序不能进行任何限制,上面程序将引发ClassCastException异常。

使用泛型

从Java5以后,Java引入了参数化类型(parameterized type)的概念,允许程序在创建集合时指定集合元素的类型。Java参数化类型被称为泛型(Generic)。

创建这种特殊集合的方法是:在集合接口 、类后增加尖括号,尖括号里放一个数据类型,即表明这个集合接口、集合类只能保存特定类型的对象。从而使集合自动记住所有集合元素的数据类型,从而无须对集合元素进行强制类型转换。

Java9增强的”菱形“语法

在Java7以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型,这显得有些多余了。例如如下两条语句。

List<String> strList = new ArrayList<String>();
Map<String, Integer> scores = new HashMap<String, Integer>();

上面两条语句中的构造器后面的尖括号部分完全是多余的,在Java7以前这是必需的,不能省略。从Java7开始,Java允许在构造器后不需带完整的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息。上面两条代码可以改写为如下形式。

List<String> strList = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();

Java9再次增强了”菱形“语法,它甚至允许在创建匿名内部类时使用菱形语法,Java可根据上下文来推断匿名内部类中泛型的类型。下面代码示范了在匿名内部类中使用菱形语法。

interface Foo<T> {
   
	void test(T t);
}

public class AnnoymousDiamond {
   

	public static void main(String[] args) {
   
		// 指定Foo类中泛型为String
		Foo<String> f = new Foo<>() {
   
			// test()方法的参数类型为String
			public void test(String t) {
   
				System.out.println("test方法的t参数为: " + t);
			}
		};
		// 使用泛型通配符,此时相当于通配符的上限为Object
		Foo<?> fo = new Foo<>() {
   
			// tes()方法的参数类型为Object
			public void test(Object t) {
   
				System.out.println("test方法的Object参数为: " + t);
			}

		};
		// 使用泛型通配符,通配符的上限为Number
		Foo<? extends Number> fn = new Foo<>() {
   
			// 此时test()方法的参数类型为Number
			public void test(Number t) {
   
				System.out.println("test方法的Number参数为: " + t);
			}
		};
	}

}

上面的代码定义了带泛型声明的接口。

深入泛型

所谓泛型,就是允许在定义类、接口、方法时使用类型的形参,这个类型形参(或叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。Java5改写了集合框架中的全部接口,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。

定义泛型接口、类

下面是Java5改写后List接口、Iterator接口、Map的代码片段。

// 定义接口时制定了一个泛型形参,该形参名为E
public interface List<E> {
   
    // 在该接口里,E可作为类型使用
    // 下面方法可以使用E作为参数类型
    void add(E x);
    Iterator<E> iterator();
    ...
}
// 定义接口时指定了一个泛型形参,该形参名为E
public interface Iterator<E> {
   
    // 在该接口里E完全可作为类型使用
    E next();
    boolean hasNext();
    ...
}
// 定义接口时指定了一个泛型形参,该形参名为E
public interface Map<K, V> {
   
    // 在该接口里K、V完全可作为类型使用
    Set<K, V> keySet();
    V put(K key, V value);
    ...
}

允许在定义接口、类时声明泛型形参,泛型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种泛型形参。

可以为任何类、接口增加泛型声明,并不是只有集合类才可以使用泛型声明。

从泛型类派生子类

当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,当使用这些接口、父类时不能再包含泛型形参。

方法中的形参代表变量、常量、表达式等数据。定义方法时可以声明数据形参,调用方法时必须为这些数据形参传入实际的数据;与此类似的是,定义类、接口、方法时可以

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值