泛型问题的引出
- 泛型从JDK1.5之后追加到Java语言里面的,其主要目的是为了解决ClassCastException的问题,在进行对象的向下转型时都可能存在安全隐患,而JAVA希望通过泛型可以慢慢解决掉此类问题;
- 假设定义一个描述x和y坐标的处理类,并且在这个类之中允许开发者保存有三类数据:
- 整型数据:x = 10、y = 20;
- 浮点型数据: x = 10.1、y = 20.9;
- 字符串型数据:x = 东经120度、y = 北纬30度;
- 于是在设计Point类的时候就需要去考虑具体的x和y属性的类型,这个类型要求可以保存以上三种数据,很明显,最为原始的做法就是利用Object类来进行定义,因为存在有下面的转型关系:
- 整型数据:基本数据类型 => 包装成Integer类对象 => 自动向上转型为Object
- 浮点型数据:基本数据类型 => 包装成Double类对象 => 自动向上转型为Object
- 字符串型数据:String类对象 => 自动向上转型为Object
//定义Point类
class Point {
private Object x;
private Object y;
public void setX(Object x) {
this.x = x;
}
public void setY(Object y) {
this.y = y;
}
public Object getX() {
return this.x;
}
public Object getY() {
return this.y;
}
}
//然后进行内容设置
public class JavaDemo {
public static void main(String [] args) {
Point point = new Point();
//第一步:根据需求进行内容设置
point.setX(10);
point.setY(20);
//第二步:从里面获取数据
int x = (Integer) point.getX();
int y = (Integer) point.getY();
System.out.println("x = " + x + "、y = " + y);
}
}
- Object可以接收的范围太广了,如果将任意类型的值赋给x或y都可以,然后向下转型时就要格外注意,所以为了避免此类问题,才有了泛型;
泛型基本定义
- 泛型的本质在于,类中的属性或方法的参数与返回值的类型可以由对象实例化的时候动态决定。此时就需要在类定义的时候明确定义占位符(泛型标记);
class Point <T>{
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return this.x;
}
public T getY() {
return this.y;
}
}
- 此时Point类中的x与y属性的数据类型并不确定,而是由外部来决定;
- 关于默认的泛型类型
- 由于泛型是属于JDK1.5之后的产物,但是在这之前已经有不少内置的程序类或者是接口广泛的应用在项目开发之中,于是为了保证这些类或者接口追加了泛型之后,原始的程序类依然可以使用,所以如果不设置泛型时,自动将使用Object类作为类型,以保证程序的正常执行,但是在编译的时候会出现警告信息;
- 泛型定义完成后可以在实例化对象的时候进行泛型类型的设置,一旦设置之后里面的x和y的属性的类型就与当前对象直接绑定了;
public class JavaDemo {
public static void main(String [] args) {
Point <Integer> point = new Point <Integer> ();
//第一步:根据需求进行内容设置
point.setX(10);
point.setY(20);
//第二步:从里面获取数据
int x = point.getX();
int y = point.getY();
System.out.println("x = " + x + "、y = " + y);
}
}
- 由于Point类里面设置的泛型为Integer,这样所有的对应此泛型的属性、变量、方法返回值将全部替换为Integer(只局限于此对象之中);
- 泛型的使用注意点:
- 泛型之中只允许设置引用类型,如果要操作基本类型,必须使用包装类;
- 从JDK1.7开始,泛型对象实例化可以简化为Point <Integer> point = new Point <> ();
- 使用泛型可以解决大部分的类对象的强制转换处理,这样的程序才是一个合理的设计;
泛型通配符
- 虽然泛型帮助开发者解决了一系列的对象的强制转换所带来的安全隐患,但是从另外一个角度来讲,泛型也带来了一些新的问题:
class Message <T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return this.content;
}
}
public class JavaDemo {
public static void main(String [] args) {
Message <String> msg = new Message <String> ();
msg.setContent("www.mldn.com");
fun(msg);
}
public static void fun(Message <String> temp) {
System.out.println(temp.getContent());
}
}
- 但是这个时候问题也就出现了,问题出现在了我们的fun()方法上,如果真的去使用泛型,不可能是一种类型,也就是说fun()方法应该可以接收任意种泛型类型的Message对象,但是这个时候只能接收Message<String>类型,这种情况下,就要使用通配符;
public class JavaDemo {
public static void main(String [] args) {
Message <String> msgA = new Message <String> ();
Message <Integer> msgB = new Message <Integer> ();
msgA.setContent("www.baidu.com");
fun(msgA);
msgB.setContent(10);
fun(msgB);
}
public static void fun(Message <?> temp) {
System.out.println(temp.getContent());
}
}
- 此时在fun()方法里面由于采用了Message结合通配符的处理所以可以接收所有的类型,并且不允许修改只允许获取数据;
- 在 <?> 通配符的基础之上实际上还提供有两类小的通配符:
- <? extends 类> :设置泛型的上限,<? extends Number> 表示该泛型类型只允许设置Number或Number的子类;
- <? super 类> : 设置泛型的下限,<? super String> 只能够使用String或其父类;
//观察泛型的上限配置
class Message <T extends Number> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return this.content;
}
}
public class JavaDemo {
public static void main(String [] args) {
Message <Integer> msgB = new Message <Integer> ();
msgB.setContent(10);
fun(msgB);
}
public static void fun(Message <? extends Number> temp) {
System.out.println(temp.getContent());
}
}
//设置泛型下限
class Message <T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return this.content;
}
}
public class JavaDemo {
public static void main(String [] args) {
Message <String> msgB = new Message <String> ();
msgB.setContent("www.baidu.com");
fun(msgB);
}
public static void fun(Message <? super String> temp) {
System.out.println(temp.getContent());
}
}
泛型接口
- 泛型除了可以在类上定义之外也可以直接在接口之中进行使用
//定义泛型接口
interface IMessage <T> {
public abstract String echo(T t);
}
//实现方式一:在子类中继续设置泛型定义;
interface IMessage <T> {
public abstract String echo(T t);
}
class MessageImpl<S> implements IMessage<S> {
public String echo(S t) {
return "【echo】" + t;
}
}
public class JavaDemo {
public static void main(String [] args) {
IMessage<String> msg = new MessageImpl<String>();
System.out.println(msg.echo("www.baidu.com"));
}
}
//实现方式二:在子类实现父接口的时候直接定义出具体泛型类型;
interface IMessage <T> {
public abstract String echo(T t);
}
class MessageImpl implements IMessage<String> {
public String echo(String t) {
return "【echo】" + t;
}
}
public class JavaDemo {
public static void main(String [] args) {
IMessage<String> msg = new MessageImpl();
System.out.println(msg.echo("www.baidu.com"));
}
}
泛型方法
- 在之前的程序类里面实际上已经可以发现在泛型类之中如果将泛型标记写在了方法上,那么这样的方法就被成为泛型方法,但是需要注意的是,泛型方法不一定非要出现在泛型类之中,即:如果一个类中没有定义泛型,那么也可以使用泛型方法;
public class JavaDemo {
public static void main(String [] args) {
Integer [] num = fun(1, 2, 3); //传入了整数,泛型类型就是Integer
for(int temp : num) {
System.out.println(temp);
}
}
public static <T> T [] fun(T ...args) {
return args;
}
}

此图来源于李兴华老师

此图来源于李兴华老师
//利用泛型改进工厂
//此程序还有问题,日后再改
interface IMessage {
public abstract void send(String str);
}
class MessageImpl implements IMessage {
public void send(String str) {
System.out.println("消息发送" + str);
}
}
class Factory {
public static <T> T getInstance(String className) {
if("MessageImpl".equals(className)) {
return (T) new MessageImpl();
}
return null;
}
}
public class JavaDemo {
public static void main(String [] args) {
IMessage msg = Factory.getInstance("MessageImpl");
msg.send("www.baidu.com");
System.out.println(IMessage.class);
}
}
网友评论