1. 首页
  2. Java基础

043-四十三、Java之泛型详解

泛型程序设计

意味着编写的代码可以被很多不同类型的对象所重用。
例如, 我们并不希望为聚集 StringFile 对象分别设计不同的类。

实际上,也不需要这样做,因为一个 ArrayList 类可以聚集任何类型的对象。这是一个泛型程序设计的实例。

类型参数的好处

Java 中增加范型类之前, 泛型程序设计是用继承实现的。ArrayList 类只维护一个 Object 引用的数组(ArrayList源码)


public class ArrayList { private Object[] elementData; public Object get(int i) { . . , } public void add(Object o) { . . . } } 这种方法有两个问题。当获取一个值时必须进行强制类型转换。 ArrayList files = new ArrayList(); files.add("hello"); //在这里必须强转成String类型,否则是默认的Object类型,不强转就会编译不通过! String filename = (String)files.get(0); //这里就是String类型了,因为上面强转了 System.out.println(filename); 此外,这里没有错误检査。可以向数组列表中添加任何类的对象。 ArrayList files = new ArrayList(); //这里添加的是一个字符串 files.add("hello"); String filename1 = (String)files.get(0); System.out.println(filename1); //如果现在添加的是一个对象(ArrayList)也是没有问题的。 files.add(new ArrayList()); //这个元素应该是一个ArrayList数组,那么我们强转成String类型而系统也会编译通过的! String filename2 = (String)files.get(1); //这里就是String类型了,因为上面强转了 System.out.println(filename2); 以上编译器照样编译通过!但是当代码运行到 String filename2 = (String)files.get(1); 这行代码的时候就会报一个异常强转String类型失败!!! Exception in thread "main" java.lang.ClassCastException: java.base/java.util.ArrayList cannot be cast to java.base/java.lang.String at 集合.myarraylist.ArrayListTest.main(ArrayListTest.java:16)

那么目前泛型就可以解决这种问题了。下面我们来看看泛型怎么来解决这个问题。

泛型指定类型

泛型提供了一个更好的解决方案:
类型参数 ( type parameters)。 ArrayList 类有一个类型参数用来指示元素的类型,也使得代码具有更好的可读性。人们一看就知道这个数组列表中包含的是 String 对象。

改造上面的代码:


//为ArrayList指定存储的元素类型为字符串:String ArrayList<String> files = new ArrayList<>(); //这里只能添加字符串了 files.add("hello"); //这里就不需要在转型了 String filename1 = files.get(0); System.out.println(filename1); //这里在添加其它类型的就不可以了,编译器就不会通过了。 files.add(new ArrayList());

这里就不会出现以上转型问题了。编译器也可以很好地利用这个信息。当调用get的时候, 不需要进行强制类型转换,编译器就知道返回值类型为 String,而不是Object。

然后add方法添加类型也会固定为String了,如果add其它类型是无法通过编译的。

泛型类

一个泛型类(generic class) 就是具有一个或多个类型变量的类。

泛型类的声明和非泛型类的声明类似,就是在类名后面添加了类型参数声明部分,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。

一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为它们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

泛型语法:


public class 类名称<类型参数>{ ... } 示例: public class MyClass<T> { private T t; /** * 设置t的值 * @param t */ public void set(T t) { this.t = t; } /** * 获取t的值 * @return */ public T get() { return t; } public static void main(String[] args) { //实例化一个integerMyClass对象,泛型的参数类型为Integer MyClass<Integer> integerMyClass = new MyClass<Integer>(); //实例化一个stringMyClass对象,泛型的参数类型为String MyClass<String> stringMyClass = new MyClass<String>(); //设置t的值为:new Integer(10) integerMyClass.set(new Integer(10)); //设置t的值为:new String("这个泛型类是指定的String类型") stringMyClass.set(new String("这个泛型类是指定的String类型")); System.out.printf("整型值为 :%d\n\n", integerMyClass.get()); System.out.printf("字符串为 :%s\n", stringMyClass.get()); } }

备注:其实大家也可以把泛型类看作普通类的工厂。

泛型方法

前面已经介绍了如何定义一个泛型类。实际上,还可以定义一个带有类型参数的简单方法,这就是泛型方法。泛型方法可以定义在普通类中,也可以定义在泛型类中。

泛型方法定义:


public static <T> T getMiddle(T... a) { return a[a.length / 2]; }

所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。

每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。

类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。

泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。


示例: public class TestArrayList { /** * 泛型方法printArray1:只能传一个参数的泛型方法 * @param inputArray * @param <E> */ private <E> void printArray1(E inputArray) { //输出数组元素 System.out.printf( "printArray1:%s ", inputArray); System.out.println(); } /** * 泛型方法printArray1:可以传入多个参数的泛型方法 * @param inputArray * @param <E> */ private static <E> void printArray2(E... inputArray ) { System.out.print("printArray2:"); //输出数组元素 for ( E element : inputArray ){ System.out.printf( "%s ", element); } System.out.println(); } public static void main(String[] args) { /** * 这里在调用方法,在方法名前的尖括号中放人具体的类型 * 但是往往这个类型我们是可以去掉的 */ new TestArrayList().<String>printArray1("zhangsan"); printArray2("wangwu",1,1.3f); } } 运行的结果 printArray1:zhangsan printArray2:wangwu 1 1.3

类型变量的限定

有时,类或方法需要对类型变量加以约束。我们来看看下面的例子再来讲解。


public static <T> T min(T[] a) { if (a == null || a.length == 0) { return null; } T smallest = a[0] ; for (int i = 1; i < a.length; i ++){ if (smallest.compareTo(a[i]) > 0){ smallest = a[i]; } } return smallest; }

这里该方法本身就会编译不通过但是又想传入的类型都包含compareTo方法怎么办?

现在该方法变量 smallest 类型为 T, 这也意味着它可以是任何一个类的对象。怎么才能确信T所属的类有 compareTo 方法呢?

那么这个时候我们就可以通过对类型变量T设置限定解决。


public static <T extends Comparable> T min(T[] a)

这样泛型方法限定了必须传入的参数都是实现了 Comparable 接口的类或者子类,如果传入其它对象的话 min 将会产生一个编译错误。

类型通配符

1、类型通配符一般是使用?代替具体的类型参数。例如 List&lt;?> 在逻辑上是 List&lt;String>,List&lt;Integer> 等所有List&lt;具体类型实参> 的父类。


示例: public class TestArrayList { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); getData(name); getData(age); getData(number); } /** * 类型通配符就是<?>这个?代表这任意类型 * @param data */ public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } }

写完了如果写得有什么问题,希望读者能够给小编留言,也可以[点击此处扫下面二维码关注微信公众号](https://www.ycbbs.vip?p=28 "点击此处扫下面二维码关注微信公众号")

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「方志朋」,公众号后台回复「666」 免费领取我精心整理的进阶资源教程
  4. JS中文网,Javascriptc中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,是给开发者用的 Hacker News,技术文章由为你筛选出最优质的干货,其中包括:Android、iOS、前端、后端等方面的内容。目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。

    本文著作权归作者所有,如若转载,请注明出处

    转载请注明:文章转载自「 Java极客技术学习 」https://www.javajike.com

    标题:043-四十三、Java之泛型详解

    链接:https://www.javajike.com/article/1350.html

« 044-四十四、Java之手写ArrayList(上)
042-四十二、Java集合框架详解»

相关推荐

QR code