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

JavaSE第五十四讲:自定义泛型与泛型的常见陷阱

2012-12-27 10:16 357 查看
结合上一讲泛型的内容,现在我们来通过程序掌握两个泛型的使用

package com.ahuier.jdk5;

public class Generic<T1, T2> {
private T1 foo1;
private T2 foo2;
public T1 getFoo1() {
return foo1;
}
public void setFoo1(T1 foo1) {
this.foo1 = foo1;
}
public T2 getFoo2() {
return foo2;
}
public void setFoo2(T2 foo2) {
this.foo2 = foo2;
}
public static void main(String[] args) {
Generic<Integer, Boolean> foo = new Generic<Integer, Boolean>();

/*
* 这边两种不同的写法,得出的结果是一样的,为什么?
*/
/*		foo.setFoo1(-20);
foo.setFoo2(false);*/
foo.setFoo1(new Integer(-20));
foo.setFoo2(new Boolean(false));

/*		Integer in = foo.getFoo1();
Boolean b = foo.getFoo2();*/
System.out.println(foo.getFoo1());
System.out.println(foo.getFoo2());
}

}


编译执行结果:

-20

false

【说明】:为什么下面两种在main函数中的写法得出的结果是一样的呢?那种体现泛型呢?不明白的地方!

[方式1]foo.setFoo1(-20);

foo.setFoo2(false);

[方式2]foo.setFoo1(new Integer(-20));

foo.setFoo2(new Boolean(false));

数组泛型的使用

package com.ahuier.jdk5;

public class Generic1<T> {
private T[] fooArray;

public T[] getFooArray() {
return fooArray;
}

public void setFooArray(T[] fooArray) {
this.fooArray = fooArray;
}
public static void main(String[] args) {
Generic1<String> foo = new Generic1<String>();
String[] str = {"hello", "world", "welcome"};
foo.setFooArray(str);
String[] str1 = foo.getFooArray();
for(int i = 0; i < str1.length; i++){
System.out.println(str1[i]);
}
}
}
编译执行结果:

hello

world

welcome

使用泛型实现一个简单的集合,模仿ArrayList的使用方式

package com.ahuier.jdk5;

public class SimCollection<T> {
private T[] objArr;

private int index = 0; //数组的索引,表示当前有多少个数组

//默认数组长度,定义为10
public SimCollection(){

//		objArr = new T[10] //这种写法是错误的,如果T是具体的某一种类型,则可以,对于数组来说,不能通过new的方式来创建一个泛型数组。

/*
* objArr = (T[])new Object[10];
* 这边会出现警告:Type safety: Unchecked cast from Object[] to T[]
* 这边没有任何办法可以除去这个警告,查看ArrayList源代码,相同原理
*/
objArr = (T[])new Object[10];
}

//自定义数组长度
public SimCollection(int capacity){
objArr = (T[])new Object[capacity];
}

public void add(T t){
objArr[index++] = t;  //index表示数组索引,首个索引为0,++ 表示索引往后移动一个位置,下次就放在索引1的位置上
}
public int getLegth(){
return this.index;   //取出数组长度
}
public T get(int i){
return objArr[i];    //取出数组中的元素
}
public static void main(String[] args) {
SimCollection<Integer> c = new SimCollection<Integer>();

//往数组中添加Integer类型数组
for(int i = 0; i < 10; i++){
c.add(new Integer(i));
}
for(int i = 0; i < c.getLegth(); i++){
Integer in = c.get(i);
System.out.println(in);
}

}
}
编译执行结果:0 1 2 3 4 5 6 7 8 9

【说明】这边查看ArrayList源码中泛型的使用方式,

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
ArrayList中直接定义为Objcet[],而我们自定义的是T[],这边存在不同,继续接下来查看

ArrayList定义为Object[],那它取出来的时候也必须是Object类型的数组(Object[]),查看get()方法。

public E get(int index) {
RangeCheck(index);

return (E) elementData[index];
}
return (E) elementData[index]; 这边它返回的时侯,已经强制类型转化为泛型 E。所以这边ArrayList的设计是定义的时候,用Object[],取出来的时候转化为泛型类别 E,而我们的设计是定义数组的时候就直接定义成泛型,所以在构造函数 objArr = (T[])new Object[10]; 里面就必须要进行强制类型转化。这就是我的设计与ArrayList的设计的差异性。

【总结我的泛型定义与ArrayList的不同】ArrayList 定义为数组类型,取的时候转化为泛型,而我们的设计是定义为泛型,构造的时候强制转换为泛型,取的时候就不需要转了。

写一个泛型嵌套泛型的例子

package com.ahuier.jdk5;

public class WrapperFoo<T> {

private GenericFoo3<T> foo;

public GenericFoo3<T> getFoo() {
return foo;
}

public void setFoo(GenericFoo3<T> foo) {
this.foo = foo;
}
public static void main(String[] args) {
GenericFoo3<Integer> foo = new GenericFoo3<Integer>();
foo.setFoo(new Integer(-10));

//将泛型做为类型传递过去
WrapperFoo<Integer> wrapper = new WrapperFoo<Integer>();
wrapper.setFoo(foo);
GenericFoo3<Integer> g = wrapper.getFoo();
System.out.println(g.getFoo());
}
}

class GenericFoo3<T>{
private T foo;

public T getFoo() {
return foo;
}

public void setFoo(T foo) {
this.foo = foo;
}
}
编译执行结果:

-10

查看JDK Doc文档中的集合,可以发现从JDK5.0开始,集合都改成泛型的方式去实现了,所以我们在将来写集合的时候要使用泛型去实现,现在我们就用这种方式去写几个集合的例子。

【说明】:可能有人会觉得,如果我不使用泛型的话,我可以往集合里面放置任何类型的对象,但是如果使用了泛型,那只能使用指定的类型,但是终究这种设计是不好的,集合通常就是用来放置相同类型的对象,如果有两个不同的类型对象,那就声明两个集合。泛型官方文档表示是为了避免编译类转换异常的方式,但我个人认为它是一种规范代码,提高可读性的很好方式。

程序一:List的泛型方式实现

package com.ahuier.jdk5;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ArrayListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();

//查看Eclipse提示信息,add里面的内容只能加字符串了。
list.add("a");
list.add("b");
list.add("c");
list.add("d");

for(int i = 0; i < list.size(); i++){

String value = list.get(i); //查看Eclipse提示信息,调用get(i)返回的也String类型,所以不需要进行强制类型转换了。
System.out.println(value);
}

/*
* 使用迭代器的时候泛型也是这么用的。
* 查看JDK Doc文档,它也是泛型,表示迭代目标集合的类型。
* 集合对象是String类型,所以迭代器泛型也是String
*/
for(Iterator<String> iter = list.iterator(); iter.hasNext();){
String value = iter.next(); //查看Eclipse提示信息,调用next()方法返回的是也是String类型,所以不需要进行强制类型转换了。
System.out.println(value);
}
}
}

编译执行结果:a b c d a b c d

程序二:Set的泛型方式实现

package com.ahuier.jdk5;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("aa");
set.add("bb");
set.add("cc");
set.add("dd");
for(Iterator<String> iter = set.iterator(); iter.hasNext();){
String value = iter.next();
System.out.println(value);
}
System.out.println("-----------------------------");
Set<People> set2 = new HashSet<People>();
set2.add(new People("zhangsan", 10, "shanghai"));
set2.add(new People("lisi", 20, "guangdong"));
set2.add(new People("wangwu", 30, "beijing"));
for(Iterator<People> iter = set2.iterator(); iter.hasNext();){
People people = iter.next();
//	System.out.println(value);//这边打印出来不是内容值,使用getter() 和 setter()方法取。
String name = people.getName();
int age = people.getAge();
String address = people.getAddress();
System.out.println(name + "," + age + "," + address);
}
}
}

/*
* 自定义一个类,实现set的泛型
*/
class People{
private String name;
private int age;
private String address;
public People(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
/*
* 为了能够将其放入集合中,必须为它们实现hashCode()和equals()方法,不懂请查看前几讲内容
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People other = (People) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
/*
* 添加getter() 和 setter()方法,便于上面取值。
*/
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}

}
编译执行结果:

dd

aa

bb

cc

-----------------------------

wangwu,30,beijing

lisi,20,guangdong

zhangsan,10,shanghai

程序三:Map的泛型方式实现

package com.ahuier.jdk5;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest {
public static void main(String[] args) {
/*
* 查看JDK Doc文档,Map<K, V>是定义了两个泛型
*/
Map<String, String> map = new HashMap<String, String>();
map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");
map.put("d", "dd");

/*
* Map的遍历有两种方式,参考之前所讲内容
*/
//法一:获取key,遍历key得到value值,注意key返回的也是用String类型变量接受。
Set<String> set = map.keySet();
for(Iterator<String> iter = set.iterator(); iter.hasNext();){
String key = iter.next();
String value = map.get(key);
System.out.println(key + ":" + value);
}
System.out.println("-------------------------");

//法二:调用entrySet()方法,获得一个Map.Entry对象
//调用entrySet()方法后,会返回一个Set对象,Set里面是一个一个的Map.Entry元素,Map.Entry元素封装了key和value的值,它们的字符串类型的,所以Map.Entry是一个泛型
Set<Map.Entry<String, String>> set2 = map.entrySet();
//这边迭代器也是一个泛型 Map.Entry<String,String>
for(Iterator<Map.Entry<String,String>> iter = set2.iterator(); iter.hasNext();){
Map.Entry<String, String> entry = iter.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}
}
}
编译执行结果:
d:dd

b:bb

c:cc

a:aa

-------------------------

d:dd

b:bb

c:cc

a:aa
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: