Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。实现参数的任意化。
泛型让编程人员能够使用类型抽象,通常用于集合里面(集合框架中的类都是泛型类)。
需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
在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 al = new ArrayList();
al.add( "ysjian001");
// al.add(new File()); // 定义了String类型参数,添加File对象会报错
String first = al.get(0);// 使用泛型后取值不用进行类型转换
1、定义
泛型类型在类的定义中。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
2、格式
(2.1)类的定义
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic{
//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 genericInteger = new Generic(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic genericString = new Generic("key_vlaue");
Log.d("泛型测试","key is " + genericInteger.getKey());
Log.d("泛型测试","key is " + genericString.getKey());
3、实例
// 泛型类:把泛型定义在类上
public class ObjectTool {
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 ot = new ObjectTool();
ot.setObj(new String("中国"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool ot2 = new ObjectTool();
ot2.setObj(new Integer(69));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);
}
}
1、定义
泛型方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
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 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 List newArrayList() {
return new ArrayList();
}
}
public class Test {
public static void main (String[] args) throws java.lang.Exception
{
Demo demo = new Demo();
List list = demo.newArrayList();
list.add("www.bo56.com");
list.add("bo56.com");
//list.add(1); 报错。只能添加String
for (String str:list) {
System.out.println(str);
}
}
}
1、定义
将泛型定义在接口上
2、实例
/* * 泛型接口:把泛型定义在接口上 */
public interface Inter {
public abstract void show(T t);
}
//实现类在实现接口的时候,我们会遇到两种情况
//第一种情况:已经知道是什么类型的了
public class InterImpl implements Inter {
@Override
public void show(String t) {
System.out.println(t);
}
}
//第二种情况:还不知道是什么类型的
public class InterImpl implements Inter {
@Override
public void show(T t) {
System.out.println(t);
}
}
public class InterDemo {
public static void main(String[] args) {
// 第一种情况的测试
Inter i = new InterImpl();
i.show("hello");
// 第二种情况的测试
Inter i = new InterImpl();
i.show("hello");
Inter ii = new InterImpl();
ii.show(100);
}
}
类型通配符一般是使用?代替具体的类型参数(是实参不是形参!)。例如 List> 在逻辑上是List,List 等所有List<具体类型实参>的父类。
可以解决当具体类型不确定的时候,这个通配符就是?;当操作类型时,不需要使用类型的具体功能时,只使用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();
//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);
}
}
1、介绍
List extends Number> list;
其中 extends Number>表示通配符的下边界,即“?”只能被赋值为Number或其子类型。
2、特点
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();
// List extends Fruit> listA = new ArrayList(); 编译错误。不能为父类。
List extends Fruit> listN = new ArrayList();//为泛型制定类型只能为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);
}
}
1、介绍
List super Integer> list;
其中 super Integer>表示通配符的下边界,即“?”只能被赋值为Integer或其父类型。
2、特点
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();
List super Fruit> listA = new ArrayList(); //只能指定Fruit类或其父类
//List super Fruit> listN = new ArrayList(); 编译错误,不能为子类
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); 编译错误。
}
}
public class GenericDemo {
public static void main(String[] args) {
// 泛型如果明确的写的时候,前后必须一致 Collection