Java 泛型(Generic)简介及用法详解
引入:
看下面演示代码测试
package cn.wen; import java.util.ArrayList; import java.util.Iterator; public class GenericDemo1 { public static void main(String[] args) { // 创建 ArrayList array = new ArrayList(); // 添加元素 array.add("hello"); array.add("world"); array.add("java"); //array.add(new Integer(100)); array.add(10); // JDK5以后的自动装箱 // 等价于:array.add(Integer.valueOf(10)); // 遍历 Iterator it = array.iterator(); while (it.hasNext()) { // ClassCastException String s = (String) it.next(); System.out.println(s); } } }
输出异常:
hello
world
java
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at cn.wen.GenericDemo1.main(GenericDemo1.java:49)
ArrayList存储字符串并遍历
我们按照正常的写法来写这个程序, 结果确出错了。 为什么呢?因为我们开始存储的时候,存储了String和Integer两种类型的数据。 而在遍历的时候,我们把它们都当作String类型处理的,做了转换,所以就报错了。
但是,它在编译期间却没有告诉我们。所以,就觉得这个设计的不好。
回想一下,我们的数组
String[] strArray = new String[3];
strArray[0] = "hello";
strArray[1] = "world";
strArray[2] = 10;
集合也模仿着数组的这种做法,在创建对象的时候明确元素的数据类型(字符串)。这样就不会在有问题了。
而这种技术被称为:泛型。
泛型:是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。
格式:
<数据类型>
此处的数据类型只能是引用类型。
好处:
A:把运行时期的问题提前到了编译期间
B:避免了强制类型转换
C:优化了程序设计,解决了黄色警告线
Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是可以使用 Java 泛型。使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
修改上面代码:正常输出
package cn.wen; import java.util.ArrayList; import java.util.Iterator; public class GenericDemo1 { public static void main(String[] args) { // 创建 ArrayList<String> array = new ArrayList<String>(); // 添加元素 array.add("hello"); array.add("world"); array.add("java"); //array.add(new Integer(100)); //array.add(10); // JDK5以后的自动装箱 // 等价于:array.add(Integer.valueOf(10)); // 遍历 Iterator<String> it = array.iterator(); while (it.hasNext()) { // ClassCastException //String s = (String) it.next(); //改进如下 String s = it.next(); System.out.println(s); } } }
泛型在哪些地方使用呢?
看API,如果类,接口,抽象类后面跟的有<E>就说要使用泛型。一般来说就是在集合中使用。
案例:
1)、用ArrayList存储字符串元素,并遍历。用泛型改进代码
package cn.wen_02; import java.util.ArrayList; import java.util.Iterator; public class ArrayListDemo { public static void main(String[] args) { // 用ArrayList存储字符串元素,并遍历。用泛型改进代码 ArrayList<String> array = new ArrayList<String>(); array.add("hello"); array.add("world"); array.add("java"); Iterator<String> it = array.iterator(); while (it.hasNext()) { String s = it.next(); System.out.println(s); } System.out.println("-----------------"); for (int x = 0; x < array.size(); x++) { String s = array.get(x); System.out.println(s); } } }
2)、需求:存储自定义对象并遍历。
A:创建学生类
B:创建集合对象
C:创建元素对象
D:把元素添加到集合
E:遍历集合
package cn.wen_02; import java.util.ArrayList; import java.util.Iterator; /* * 需求:存储自定义对象并遍历。 * * A:创建学生类 * B:创建集合对象 * C:创建元素对象 * D:把元素添加到集合 * E:遍历集合 */ public class ArrayListDemo2 { public static void main(String[] args) { // 创建集合对象 // JDK7的新特性:泛型推断。 // ArrayList<Student> array = new ArrayList<>(); // 但是我不建议这样使用。 ArrayList<Student> array = new ArrayList<Student>(); // 创建元素对象 Student s1 = new Student("小明", 40); Student s2 = new Student("小东", 30); Student s3 = new Student("小亮", 26); // 添加元素 array.add(s1); array.add(s2); array.add(s3); // 遍历 Iterator<Student> it = array.iterator(); while (it.hasNext()) { Student s = it.next(); System.out.println(s.getName() + "---" + s.getAge()); } System.out.println("------------------"); for (int x = 0; x < array.size(); x++) { Student s = array.get(x); System.out.println(s.getName() + "---" + s.getAge()); } } }
学生类:
package cn.wen_02; public class Student { // 姓名 private String name; // 年龄 private int age; public Student() { super(); } public Student(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
泛型由来
为什么会有泛型呢 ?
- 通过案例引入
- 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
泛型应用
- 泛型类
把泛型定义在类上
格式 :public class 类名 < 泛型类型 1,…>
注意 : 泛型类型必须是引用类型
- 泛 型方法
把泛型定义在方法上
格式 :public < 泛型类型 > 返回类型 方法名 ( 泛型类型 .)
- 泛 型接口
• 把泛型定义在接口上
• 格式 :public interface 接口名 < 泛型类型 1…>
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
泛型类:
package cn.wen_04; /* * 泛型类:把泛型定义在类上 */ public class ObjectTool<T> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } }
测试类
package cn.wen_04; /* * 泛型类的测试 */ public class ObjectToolDemo { public static void main(String[] args) { // ObjectTool ot = new ObjectTool(); // // ot.setObj(new String("风清扬")); // String s = (String) ot.getObj(); // System.out.println("姓名是:" + s); // // ot.setObj(new Integer(30)); // Integer i = (Integer) ot.getObj(); // System.out.println("年龄是:" + i); // ot.setObj(new String("林青霞")); // // ClassCastException // Integer ii = (Integer) ot.getObj(); // System.out.println("姓名是:" + ii); System.out.println("-------------"); ObjectTool<String> ot = new ObjectTool<String>(); // ot.setObj(new Integer(27)); //这个时候编译期间就过不去 ot.setObj(new String("林青霞")); String s = ot.getObj(); System.out.println("姓名是:" + s); ObjectTool<Integer> ot2 = new ObjectTool<Integer>(); // ot2.setObj(new String("风清扬"));//这个时候编译期间就过不去 ot2.setObj(new Integer(27)); Integer i = ot2.getObj(); System.out.println("年龄是:" + i); } }
实例
如下实例演示了我们如何定义一个泛型类:
public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); Box<String> stringBox = new Box<String>(); integerBox.add(new Integer(10)); stringBox.add(new String("HolleWorld")); System.out.printf("整型值为 :%d\n\n", integerBox.get()); System.out.printf("字符串为 :%s\n", stringBox.get()); } }
2、泛型方法
你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
泛型方法类:
package cn.itcast_05; //未使用泛型之前 //public class ObjectTool<T> { // // public void show(String s) { // // System.out.println(s); // // } // // // // public void show(Integer i) { // // System.out.println(i); // // } // // // // public void show(Boolean b) { // // System.out.println(b); // // } // // public void show(T t) { // System.out.println(t); // } // } /* * 泛型方法:把泛型定义在方法上 */ public class ObjectTool { public <T> void show(T t) { System.out.println(t); } }
测试泛型方法类:
package cn.wen_05; public class ObjectToolDemo { public static void main(String[] args) { // ObjectTool ot = new ObjectTool(); // ot.show("hello"); // ot.show(100); // ot.show(true); // ObjectTool<String> ot = new ObjectTool<String>(); // ot.show("hello"); // // ObjectTool<Integer> ot2 = new ObjectTool<Integer>(); // ot2.show(100); // // ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>(); // ot3.show(true); // 说明泛型类是没有问题的 // 但是呢,谁说了我的方法一定要和类的类型的一致呢? // 要是类上没有泛型的话,方法还能不能接收任意类型的参数了呢? // 定义泛型方法后 ObjectTool ot = new ObjectTool(); ot.show("hello"); ot.show(100); ot.show(true); } }
实例
下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。
public class MaximumTest { // 比较三个值并返回最大值 public static <T extends Comparable<T>> T maximum(T x, T y, T z) { T max = x; // 假设x是初始最大值 if ( y.compareTo( max ) > 0 ){ max = y; //y 更大 } if ( z.compareTo( max ) > 0 ){ max = z; // 现在 z 更大 } return max; // 返回最大对象 } public static void main( String args[] ) { System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ) ); System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) ); System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear", "apple", "orange", maximum( "pear", "apple", "orange" ) ); } }
3, 4 和 5 中最大的数为 5 6.6, 8.8 和 7.7 中最大的数为 8.8 pear, apple 和 orange 中最大的数为 pear
3、泛型接口
接口类:
package cn.wen_06; /* * 泛型接口:把泛型定义在接口上 */ public interface Inter<T> { public abstract void show(T t); }
实现类:
package cn.wen_06; //实现类在实现接口的时候 //第一种情况:已经知道该是什么类型的了 //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); } }
测试类:
package cn.wen_06; 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); } }
泛型高级(通配符)
- 泛型通配符<?>
任意类型,如果没有明确,那么就是 Object 以及任意的 Java 类了
- ? extends E
向下限定, E 及其子类
- ? super E
向上限定, E 及其父类
package cn.wen; import java.util.ArrayList; import java.util.Collection; /* * 泛型高级(通配符) * ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了 * ? extends E:向下限定,E及其子类 * ? super E:向上限定,E极其父类 */ 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 { }
到此这篇关于Java 泛型(Generic)概述及使用的文章就介绍到这了,更多相关Java 泛型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论