文章目录
1、泛型
1.1 泛型介绍
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。实现参数的任意化。
泛型让编程人员能够使用类型抽象,通常用于集合里面(集合框架中的类都是泛型类)。
需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
1.2 泛型优点
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
没有泛型时,可以往集合中添加任意类型对象。但编译器没有对类型进行控制,无法进行错误检验,买下了安全隐患。
取值的时候错误的进行了强制类型转换,导致程序运行失败。
ArrayList al = new ArrayList();
// 无法进行错误检查,任何类型对象可以添加进去,编译器和运行期都可以通过
al.add("ysjian001");
al.add(1);
al.add(new Object());
String first = (String) al.get(0);//取值时进行强制类型转换,类型转换正确,运行成功
Integer first2 = (Integer)al.get(0);//类型转换失败,运行失败
使用泛型时,会在编译时检查数据类型,防止出现运行时错误。提高了程序的可读性和安全性。
ArrayList<String> al = new ArrayList<String>();
al.add( "ysjian001");
// al.add(new File()); // 定义了String类型参数,添加File对象会报错
String first = al.get(0);// 使用泛型后取值不用进行类型转换
1.3 泛型规则
- 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
- 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
- 泛型的类型参数可以有多个。
- 泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。
- 泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(“java.lang.String”);
1.4 泛型的使用
(1)泛型类
1、定义
泛型类型在类的定义中。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
2、格式
(2.1)类的定义
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
(2.2)类的实例化
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型测试","key is " + genericInteger.getKey());
Log.d("泛型测试","key is " + genericString.getKey());
3、实例
// 泛型类:把泛型定义在类上
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
// 泛型类的测试
public class ObjectToolDemo {
public static void main(String[] args) {
ObjectTool<String> ot = new ObjectTool<String>();
ot.setObj(new String("中国"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
ot2.setObj(new Integer(69));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);
}
}
(2)泛型方法
1、定义
泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
2、格式
使用泛型方法打印不同字符串的元素
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", 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 ); // 传递一个字符型数组
}
}
3、实例
(1)有参数的泛型方法类型的确定是根据参数类型自动推导
* * 泛型方法:把泛型定义在方法上 **
public class ObjectTool {
public <T> void show(T t) {
System.out.println(t);
}
}
public class ObjectToolDemo {
public static void main(String[] args) {
// 定义泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(100);
ot.show(true);
}
}
(2)无参数的泛型方法类型是根据等号左边声明的泛型进行推导
class Demo {
public <T> List<T> newArrayList() {
return new ArrayList<T>();
}
}
public class Test {
public static void main (String[] args) throws java.lang.Exception
{
Demo demo = new Demo();
List<String> list = demo.newArrayList();
list.add("www.bo56.com");
list.add("bo56.com");
//list.add(1); 报错。只能添加String
for (String str:list) {
System.out.println(str);
}
}
}
(3)泛型接口
1、定义
将泛型定义在接口上
2、实例
/* * 泛型接口:把泛型定义在接口上 */
public interface Inter<T> {
public abstract void show(T t);
}
//实现类在实现接口的时候,我们会遇到两种情况
//第一种情况:已经知道是什么类型的了
public class InterImpl implements Inter<String> {
@Override
public void show(String t) {
System.out.println(t);
}
}
//第二种情况:还不知道是什么类型的
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
public class InterDemo {
public static void main(String[] args) {
// 第一种情况的测试
Inter<String> i = new InterImpl();
i.show("hello");
// 第二种情况的测试
Inter<String> i = new InterImpl<String>();
i.show("hello");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(100);
}
}
1.5 通配符
(1)通配符概述
类型通配符一般是使用?代替具体的类型参数(是实参不是形参!)。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。
(2)通配符特点
- 只能添加null。
- 获取的值只能赋值给Object类型。
因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。
可以解决当具体类型不确定的时候,这个通配符就是?;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
public class Test {
public static void main (String[] args) throws java.lang.Exception
{
List<?> list = new ArrayList<Fruit>();
//list.add(new Food()); 编译错误
//list.add(new Fruit()); 编译错误
//list.add(new Apple()); 编译错误
list.add(null);
//Food food = list.get(0);编译错误
//Fruit fruit = list.get(0);编译错误
//Apple apple = list.get(0);编译错误
Object object = list.get(0);
}
}
(3)子类型(带下界)通配符 extend
1、介绍
List<? extends Number> list;
其中<? extends Number>表示通配符的下边界,即“?”只能被赋值为Number或其子类型。
2、特点
- 为泛型指定的类型只能是Fruit类型或者其子类。
- 只能为其列表添加null。
- get方法获取的值只能赋值给Fruit类或者其超类。
3、实例
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
public class Test {
public static void main (String[] args) throws java.lang.Exception
{
List<? extends Fruit> list = new ArrayList<Fruit>();
// List<? extends Fruit> listA = new ArrayList<Food>(); 编译错误。不能为父类。
List<? extends Fruit> listN = new ArrayList<Apple>();//为泛型制定类型只能为Fruit或者其子类
listN.add(null);//只能为其列表添加null
//listN.add(123); 不能add
Fruit fruit = listN.get(0);
Food food = listN.get(0);//get方法获取的值只能赋值给Fruit类或超类
//Apple apple = listN.get(0); 编译错误。get获取的值,只能给父类
listN.remove(0);
}
}
(4)父类型(带上界)通配符 super
1、介绍
List<? super Integer> list;
其中<? super Integer>表示通配符的下边界,即“?”只能被赋值为Integer或其父类型。
2、特点
- 为泛型指定的类型必须为Fruit,或者其超类。
- 可以为其列表添加任意Fruit类型,或者其子类。
- get方法获取的类型,只能赋值给Object类型。
3、实例
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
public class Test {
public static void main (String[] args) throws java.lang.Exception
{
List<? super Fruit> list = new ArrayList<Fruit>();
List<? super Fruit> listA = new ArrayList<Food>(); //只能指定Fruit类或其父类
//List<? super Fruit> listN = new ArrayList<Apple>(); 编译错误,不能为子类
listA.add(new Fruit());
//listA.add(new Food()); 编译错误,不能为父类。
listA.add(new Apple());//只能添加Fruit类型或其子类
Object object = listA.get(0);//只能赋值给Object类型
//Fruit fruit = listA.get(0);编译错误。
//Food food = listA.get(0);编译错误。
//Apple apple = listA.get(0); 编译错误。
}
}
(5)实例
public class GenericDemo {
public static void main(String[] args) {
// 泛型如果明确的写的时候,前后必须一致 Collection<Object> c1 = new ArrayList<Object>();
// Collection<Object> c2 = new ArrayList<Animal>();//报错
// Collection<Object> c3 = new ArrayList<Dog>();//报错
// Collection<Object> c4 = new ArrayList<Cat>();//报错
// ?表示任意的类型都是可以的
Collection<?> c5 = new ArrayList<Object>();
Collection<?> c6 = new ArrayList<Animal>();
Collection<?> c7 = new ArrayList<Dog>();
Collection<?> c8 = new ArrayList<Cat>();
// ? extends E:向下限定,E及其子类
// Collection<? extends Animal> c9 = new ArrayList<Object>();//报错
Collection<? extends Animal> c10 = new ArrayList<Animal>();
Collection<? extends Animal> c11 = new ArrayList<Dog>();
Collection<? extends Animal> c12 = new ArrayList<Cat>();
// ? super E:向上限定,E极其父类
Collection<? super Animal> c13 = new ArrayList<Object>();
Collection<? super Animal> c14 = new ArrayList<Animal>();
// Collection<? super Animal> c15 = new ArrayList<Dog>();//报错
// Collection<? super Animal> c16 = new ArrayList<Cat>();//报错
}
}
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
(6)通配符总结
- 通过带有通配符,限制了与泛型相关方法的使用。
- 下边界通配符:可以使用返回值为泛型变量的方法;
- 上边界通配符:可以使用参数为泛型变量的方法。