1 泛型问题引出
泛型从JDK 1.5之后追加到Java语言里面的,其主要目的是为了解决ClassCastException的问题,在进行对象的向下转型时都可能存在有安全隐患,而Java希望通过泛型可以慢慢解决掉此类问题。
//此代码易出现ClassCastException
class Pointer{
private Object x;
private Object y;
public Pointer(){}
public Pointer(Object x, Object y){
this.x = x;
this.y = 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){
Pointer point = new Pointer();
point.setX(10);
point.setY("北纬10度");
int x = (Integer) point.getX();
int y = (Integer) point.getY();
System.out.println(x + " " + y);
}
}
2 泛型基本定义
如果要想避免项目之中出现ClassCastException最好的做法是可以回避掉对象的强制转换,所以在JDK 1.5之后提供有泛型技术,泛型的本质在于类中的属性或方法的参数与返回值的类型可以由对象实例化的时候动态决定。
那么此时就需要在类定义的时候明确的定义占位符(泛型标记)。
class Pointer<T>{
private T x;
private T y;
public Pointer(){}
public Pointer(T x, T y){
this.x = x;
this.y = 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;
}
}
public class JavaDemo{
public static void main(String[] args){
Pointer<Integer> point = new Pointer<Integer>();
point.setX(10);
point.setY("北纬10度");
int x = (Integer) point.getX();
int y = (Integer) point.getY();
System.out.println(x + " " + y);
}
}
提示:关于默认的泛型类型
(1)由于泛型是属于JDK 1.5之后的产物,但是在这之前已经有不少内置的程序类或者是接口广泛的应用在项目开发之中,于是为了保证这些类或接口追加了泛型之后,原始的程序类依然可以使用,所以如果不设置泛型类型时,自动将使用Object作为类型,以保证程序的正常执行,但在编译的时候出现警告信息。
泛型定义完成后可以在实例化对象的时候进行泛型类型的设置,一旦设置之后,里面的x与y的数据类型就与当前对象直接绑定了。
现在的程序代码之中,由于Pointer类里面设置的泛型类型为Integer,这样所有的对应此泛型的属性、变量、返回值都将全部替换为Integer(只局限于此对象之中)。这样在进行处理的时候如果发现程序有错误,会自动地进行错误提示,同时也避免了对象的向下转型处理(避免了安全隐患)。
泛型的使用注意点:
(1)泛型之中只允许设置引用类型,如果现在要操作基本类型必须使用包装类;
(2)从JDK 1.7开始,泛型对象实例化可以简化为Pointer point = new Pointer<>();
使用泛型可以解决大部分的类对象的强制转换处理,这样的程序才是一个合理的设计。
3 泛型通配符
虽然泛型帮助开发者解决了一系列的对象的强制转换所带来的安全隐患,但是从另外一个角度来讲,泛型也带来一些新的问题。
问题:引用传递问题
class Message<T>{
public T content;
public Message(){}
public Message(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
public void setContent(T content){
this.content = content;
}
}
public class JavaDemo{
public static void main(String[] args){
Message<String> msgA = new Message<>();
msgA.setContent("hello");
}
public static void fun(Message<String> temp){
System.out.println(temp.getContent());
}
}
此时最大的缺点,问题的关键在于fun()方法上,如果真的去使用泛型不可能只是一种类型,也就是说,fun方法可以接收任意种类型的数据。可能第一时间我们会想到重载,但是重载会存在一些参数类型问题,代码如下:
class Message<T>{
public T content;
public Message(){}
public Message(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
public void setContent(T content){
this.content = content;
}
}
public class JavaDemo{
public static void main(String[] args){
Message<String> msgA = new Message<>();
msgA.setContent("hello");
}
public static void fun(Message<String> temp){
System.out.println(temp.getContent());
}
public static void fun(Message<Integer> temp){
System.out.println(temp.getContent());
}
}
/*
JavaDemo.java:31: 错误: 名称冲突: fun(Message<Integer>)和fun(Message<String>)具有相同疑符
public static void fun(Message<Integer> temp){
^
1 个错误
*/
通过上面代码我们可以发现重载根本行不通,那么我们现在可能会考虑用只用Message,这样写的确没问题,但是会存在安全隐患,就是在函数中我们可以修改传入的数据。
public class JavaDemo{
public static void main(String[] args){
Message<String> msgA = new Message<>();
msgA.setContent("hello");
fun(msgA);
Message<Integer> msgB = new Message<>();
msgB.setContent(12);
fun(msgB);
}
public static void fun(Message temp){
temp.setContent(100);
System.out.println(temp.getContent());
}
}
那么我们究竟应该如何做呢?答案是使用泛型通配符?。
public class JavaDemo{
public static void main(String[] args){
Message<String> msgA = new Message<>();
msgA.setContent("hello");
fun(msgA);
Message<Integer> msgB = new Message<>();
msgB.setContent(12);
fun(msgB);
}
public static void fun(Message<?> temp){
System.out.println(temp.getContent());
}
}
此时在fun()方法里面由于采用了Message结合通配符的处理所以可以接受所有的类型。
在“?”这个通配符的基础之上实际上还提供有两个小的通配符:
(1)? extends 类:设置泛型的上限;
|——例如:定义“? extends Number”:表示该泛型类型只允许设置Number或Number的子类;
(2)? super 类:设置泛型的下限;
|——例如:定义“? super String”:表示只能使用String或其父类。
范例:观察泛型上限配置
class Message<T extends Number>{
public T content;
public Message(){}
public Message(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
public void setContent(T content){
this.content = content;
}
}
public class JavaDemo{
public static void main(String[] args){
Message<Integer> msgB = new Message<>();
msgB.setContent(12);
fun(msgB);
}
public static void fun(Message<? extends Number> temp){
System.out.println(temp.getContent());
}
}
范例:设置泛型的下限
public class JavaDemo{
public static void main(String[] args){
Message<String> msgB = new Message<>();
msgB.setContent("hello");
fun(msgB);
}
public static void fun(Message<? super String> temp){
System.out.println(temp.getContent());
}
}
对于通配符而言是一个重要的概念,并且要求你一定可以理解此概念的定义,在日后学习Java一些系统类库的时候会见到大量的通配符使用。
4 泛型接口
泛型除了可以在类上定义之外也可以直接在接口之中进行使用。定义代码如下:
interface IMessage<T>{
public abstract String echo(T t);
}
对于泛型接口的子类而言现在就有两种实现方式。
实现方式一:在子类中继续设置泛型定义
interface IMessage<T>{
public abstract String echo(T t);
}
class Message<T> implements IMessage<T>{
public String echo(T t){
return "ECHO: " + t;
}
}
public class JavaDemo{
public static void main(String[] args){
IMessage<String> msg = new Message<String>();
System.out.println(msg.echo("Hello"));
}
}
实现方式二:在子类实现父接口的时候直接定义出具体泛型类型
interface IMessage<T>{
public abstract String echo(T t);
}
class Message implements IMessage<String>{
public String echo(String t){
return "ECHO: " + t;
}
}
public class JavaDemo{
public static void main(String[] args){
IMessage<String> msg = new Message();
System.out.println(msg.echo("Hello"));
}
}
5 泛型方法
在泛型类之中如果将泛型标记写在了方法上,那么这样的方法就被称为泛型方法,但是泛型方法不一定要出现在泛型类之中,即如果一个类上没有定义泛型,也可以使用泛型方法。
public class JavaDemo{
public static void main(String[] args){
Double[] n = fun(1.0, 2.0, 3.0);
for(double temp : n){
System.out.println(temp);
}
}
public static <T> T[] fun(T ...t){
return t;
}
}
项目开发时这种泛型方法很常见。