《疯狂Java讲义》-泛型(Generic)

Java的泛型-Java的参数化类型

1、构造器:
当创建带有泛型声明打自定义类的时候,构造器名还是原来的类名,不要增加泛型声明,而再使用的时候,可以增加泛型声明来调用。比如说,Apple类,其构造器名仍然为Apple,而不是Apple,但在调用的时候,可以使用Apple的形式来调用。

2、从泛型类派生子类
创建了带泛型声明的接口、父类之后,可以继承接口或父类,但要注意的是,在使用的时候不能再包含类型形参,比如下面的是错误的:

public class A extends Apply<T>{}

此处正确的写法应该为

public class A extends Apple<String>或者 public class A extends Apple

需要注意的是,如果使用public class A extends Apple<String>则其中所有的T类型形参都会被替换成String,则在子类实现的时候就需要我们特别注意类型的转换。(原来T的位置已经全部更换为String了)

3、泛型的类型擦除

import java.util.ArrayList;
import java.util.List;
public class TestGeneric {
public static void main(String[] args)
{
List listInt=new ArrayList();
List listString=new ArrayList();
System.out.println(listInt.getClass()==listString.getClass());
System.out.println(listInt.getClass().gettitle());
System.out.println(listString.getClass().gettitle());
}
}
运行结果为:
true
java.util.ArrayList
java.util.ArrayList
可以看出,其实在本质上,其都是ArrayList类,JVM运行时,并不存在泛型。

4、Foo是Bar的子类,那G<Foo>是G<Bar>的子类?

这个问题是在泛型里面需要特别注意的问题,Foo[]是Bar的子类型,但G<Foo>并不是G<Bar>的子类型。

在Java中,使用泛型的时候,只要在编译的时候没有出现警告,那在运行的时候,就不会出现 ClassCastException!

有这样的代码

List iList=new ArrayList();
List nList=iList;
nList.add(0.5);

在编译的时候会出现如下错误!

错误: 不兼容的类型
List nList=iList;
需要: List
找到:    List
1 个错误

4-1.类型通配符 ?

将问号作为通配符传入函数或者类,那么就可以表示,该元素类型可以匹配任何类型,如以下的代码:

import java.util.ArrayList;
import java.util.List;
public class TestGeneric {	
	public static void main(String[] args)
	{
		List iList=new ArrayList();
		iList.add(1);
		iList.add(2);
		iList.add(3);
		iList.add(4);		
		test(iList);		
	}
	public static void test(List<?> list)
	{
		for(int i = 0 ; i < list.size() ; i++)
		{
			System.out.println(list.get(i));
		}
	}
}

运行结果为

1
2
3
4

此处

public static void test(List<?> list);

该函数中list就使用了通配符?,用来代表任意类型,如果将此处的问号改为Number,虽然Number是Integer的父类,但仍然编译不成功。

我们需要注意的是,这种带通配符的List表示的是各种泛型List的父类,并不能将元素加入其中,比方说,下面的代码会出现错误。

List<?> c = new ArrayList();
c.add(new Object());

此处的问题也很明显,我们不知道List c中的元素的类型,因此我们无法向其中添加对象。同时分析源代码可以看到List中add方法的原型为

boolean add(E e);

因此我们可以注意到,添加的元素必须是E的类或者是其子类,此处使用通配符来替换,连类型都不清楚,当然无法添加。

4-2、设定类型通配符的上限

有如下代码:

TestGeneric.java

package testgeneric;
import java.util.ArrayList;
import java.util.List;
public class TestGeneric {
	public static void main(String[] args)
	{
		List circleList = new ArrayList();
		circleList.add(new Circle());
		MyCanvas mycanvas=new MyCanvas();
		mycanvas.drawAll(circleList);

		List shapeList = new ArrayList();
		shapeList.add(new Circle());
		shapeList.add(new Rectangle());
		mycanvas.drawAll(shapeList);
	}
}
Shape.java

package testgeneric;
public abstract class Shape {
	public abstract void draw(MyCanvas c);
}
Circle.java

package testgeneric;
import java.awt.Canvas;

public class Circle extends Shape {
	@Override
	public void draw(MyCanvas c) {
		// TODO Auto-generated method stub
		System.out.println("在画布" + c + "画一个圆");
	}
}
Rectangle.java

package testgeneric;
import java.awt.Canvas;

public class Rectangle extends Shape{
	@Override
	public void draw(MyCanvas c) {
		// TODO Auto-generated method stub
		System.out.println("在画布"+c+"画一个矩形");
	}

}
package testgeneric;
import java.util.List;

public class MyCanvas {
	public void drawAll(List<? extends Shape> shape)
	{
		for(Shape s:shape)
		{
			s.draw(this);
		}
	}
}

以上是完整的代码,注意到再MyCanvas类中,有如下一行 List<? extends Shape>,这一行就是一个受限的通配符,其意思就代表该未知类型是Shape类或者Shape的一个子类,即此处是上限(upper bound)

4-3 设置类型形参的上限

有如下代码:

package testgeneric;

public class Apple {
	T tmp;
	public static void main(String[] args)
	{
		Apple ai = new Apple();
		Apple ad = new Apple();

                下面这句将引起编译错误
		Apple as = new Apple();
	}
}

如果需要多继承的话,可以这样写

public class Apple

此处需要注意的是,类必须放在第一位上。

About: happyhls


发表评论

电子邮件地址不会被公开。 必填项已用*标注