您的位置:首页 > 编程语言 > Java开发

Java 里的泛型简介.

2014-06-08 23:37 405 查看
我们在JDK中有时回见到1个类or接口后面跟这1个尖括号.

例如:

java.util.HashMap<K,V>


我们一开始大概知道K,V 大概就是Key和Value的意思, 键值对嘛, 但是不知道这个特性对编程的影响.

其实这个设计Java的泛型问题. 把本文看完就明白了.

一. Java里泛型的定义

在数据结构中, 泛型(generic)这个词代表一种编程思想.

它的意思就是: 不论存储结构如何差异, 都可以令其具有看起来一样的操作方法.

例如 ArrayList 和 LinkList 这两个容器, 它们在内存内的存储结构是完全不同的.

但是它们的操作方法基本上是一样的.

都具有相同的,add() 方法, remove()方法, size()方法等等.

这就是泛型的编程思想.

但是在Java里不能直接操作内存, Java里泛型是另一种定义:

java里泛型用于限制传入容器和接口的数据类型.

二. 容器里的泛型.

我们打开Java JDK API, 查看ArrayList这个类

会见到下面的字眼

java.util

ArrayList<E>

ArrayList后面有个尖括号, 里面一个大写E.

其实E代表的是Element的意思, 它告诉我们, ArrayList可以用泛型来限制放入容器里的对象类型.

2.1 假如不使用泛型

假如我们不适用泛型

代码是这样写的:

import java.util.ArrayList;

class Student{
int id;
private String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}

public String toString(){
return this.id + ": " + this.name;
}
}

public class ArrayList1{
public static void main(String args[]){
ArrayList arr = new ArrayList();
arr.add(new Student(1,"Jack"));
arr.add(new Student(2,"Bill"));
arr.add(1);
arr.add("Jack");

Student st = arr.get(1);
System.out.println(st);

}

}


上面的代码中, arr这个容器添加了两个Student对象, 1个integer对象(自动装箱),1个String对象.

但是编译失败

原因在于这行代码:

Student st = arr.get(1);


在JDK API中, get()方法是这样定义的:

get

public E get(int index)

返回此列表中指定位置上的元素。

其实上面的定义告诉我们, 可以使用泛型来决定get()方法的返回值类型, 但是如果不使用泛型, 返回的类型是object.

上面用1个Student的引用st 来接收1个Object类型对象, 所以会编译失败.

所以我们需要强制转换, 将出错的代码改为:

Student st = (Student)arr.get(1);


这样就编译通过了.

但是编译信息有两句warning:

Note: ArrayList1.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.


它告诉我们这样是不安全的, 因为没有使用泛型, arr容器内还可能有其它的类型的对象.

一旦我把上面的get方法里的参数从1改成2, 则会发生转会失败, 抛出异常..

2.2 使用泛型:

我们可以在定义1个容器时使用泛型, 方法很简单, 就是在容器类名后面加上尖括号,后面写上你要放入的对象类名.

例如:

Arraylist<Student> arr = new ArrayList<Student>();


修改后的代码如下:

import java.util.ArrayList;

class Student{
int id;
private String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}

public String toString(){
return this.id + ": " + this.name;
}
}

public class ArrayList2{
public static void main(String args[]){
ArrayList<Student> arr = new ArrayList<Student>();
arr.add(new Student(1,"Jack"));
arr.add(new Student(2,"Bill"));
//arr.add(1);
//arr.add("Jack");

Student st = arr.get(1);//Student st = (Student)arr.get(1);
System.out.println(st);

}

}


上面的 get()方法已经不需要强制转换了.

值得注意的是, 当使用了泛型以后, 上面的arr容器就不能再放入除Student对象之外的其他类型元素. 否则会报错!

三. 接口里的泛型.

我地再一次打开JDKAPI, 看看Comparable接口的定义:

ava.lang

接口 Comparable<T>

类型参数:
T
- 可以与此对象进行比较的那些对象的类型

然后再看看Comparable里面最重要的方法:

int
compareTo(T o)


比较此对象与指定对象的顺序。
里面都有1个T, 意思也是可以利用泛型来指定传入Comparable接口里方法的参数类型:

3.1 假如不使用泛型:

如果不使用泛型, 那么使用实现了Comparable接口的对象里的comparaTo方法里就能接受各种类型的对象, 必须使用强制转会:

例子:

import java.lang.Comparable;

class Student implements Comparable{
int id;
private String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}

public String toString(){
return this.id + ": " + this.name;
}

public int compareTo(Object o){
Student st = (Student)o;
return this.id - st.id;
}
}

public class Comparable1{
public static void main(String args[]){
Student jack = new Student(1,"Jack");
Student bill= new Student(2,"Bill");

System.out.println(bill.compareTo(jack));
}
}


例如上面的例子

类Student 实现了Comparable接口, 重写comparaTo时的参数必须是对象Object

内容里还必须强制转会:

Student st = (Student)o;


这样写虽然看起来ok, 但是实际上也不安全

因为这样写, Student对象调用compareTo方法时就能放入各种类型的参数.

编译时是无错的,但是执行时就有可能转换失败了.

3.2 假如使用泛型.

接口使用泛型的方法也很简单:

只需要在接口名后面加上<参数类名>就ok

修改后的例子:
import java.lang.Comparable;

class Student implements Comparable<Student>{
int id;
private String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}

public String toString(){
return this.id + ": " + this.name;
}

public int compareTo(Student s){
return this.id - s.id;
}
}

public class Comparable2{
public static void main(String args[]){
Student jack = new Student(1,"Jack");
Student bill= new Student(2,"Bill");

System.out.println(bill.compareTo(jack));
//System.out.println(bill.compareTo("jack")); compile error
}
}


上面修改后, 重写的compareTo方法参数就应该指定为Student类.

而且不能将其他类型对象作为参数调用, 否则编译出错.

四. 使用泛型的意义

通过上面的例子, 我们可以得出使用java泛型 有如下好处:

1.避免强制转换的代码

2.限制传入容器的对象类型 和 接口方法参数的类型

3.避免了编译通过, 但是因为强制转换失败而执行出错的情况. 令java程序更加安全健壮.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: