引入泛型的目的是解决ClassCastException问题在进行对象的向下转型时可能存在安全隐患以及大部分的类对象的强制转换处理,而Java希望通过泛型可以慢慢解决掉此问题。
首先,泛型的问题引出:比如我们有一个Point类,这时我们的x坐标和y坐标在不同的情况需要不同的数据类型,我们应该怎么解决?
一般呢我们会使用Object类,进行对象的向下转型,但是有时会出现ClassCastException问题,因此者不是一个好的解决方法,于是泛型就应运而生。
泛型的本质在于:类中的属性或方法的参数与返回值的类型可以由对象实例化时动态决定。那么此时就需要在类定义的时候明确的定义占位符(泛型)标记。
一、定义一个Point泛型,如以下程序:
class Point <T>//T表示type
{
private T x;
private T y;
public Point(T x,T y){
this.x=x;
this.y=y;
}
public void getXY(){
System.out.println("x="+this.x+"y="+this.y);
}
}
此时Point类中的x和y的数据类型都不确定而是由对象实例化时动态决定。
实例化Point类,如以下程序:
public class Demo{
public static void main(String args[]){
Point <Integer> p=new Point<Integer>(10,20);
p.getXY();
}
}
由于现在的程序代码之中,由于Point类里面设置的泛型类型为Integer,这样所有对应泛型的属性、变量、方法返回值等就将全部替换为Integer,但是仅局限于此对象之中。
执行结果:
关于默认的泛型类型:由于泛型是JDK1.5之后的产物,但是在此之前有不少的内置程序类或者是接口广泛应用在项目开发之中,为保证这些类或接口在追加了泛型之后,原始的类依然可以使用,所以如果没有设置泛型的类型,自动将使用Object作为类型以保证程序的正常执行,但是在编译中会出现警告。
泛型的使用注意点:1.泛型之中只允许设置引用类型,如果现在要操作的基本类型,必须使用包装类;2.从JDK1.7开始,泛型对象实例化可以简化为Point p=new Point<>(10,20);诸如这样的形式。
二、泛型通配符“ ?”
虽然泛型帮助我们解决了一系列的对象强制装换所带来的的安全隐患,但也引入了一些新的问题——引用传递处理。
泛型的引用传递问题,如以下程序:
public class Demo{
public static void main(String args[]){
Point <Integer> p=new Point<>(10,20);
fun(p);
}
public static void fun(Point<Integer> p)
{
p.getXY();
}
}
我们知道。这个程序没毛病,但是如果是下面的程序呢?
public class Demo{
public static void main(String args[]){
Point <Integer> p1=new Point<>(10,20);
Point <String> p2=new Point<>("10","20");
fun(p1);
fun(p2);
}
public static void fun(Point<Integer> p)
{
p.getXY();
}
}
这时我们的fun(p2);就会在编译时出现问题,那有人就说可以重载fun方法,于是:
public class Demo{
public static void main(String args[]){
Point <Integer> p1=new Point<>(10,20);
Point <String> p2=new Point<>("10","20");
fun(p1);
fun(p2);
}
public static void fun(Point<Integer> p)
{
p.getXY();
}
public static void fun(Point<String> p)
{
p.getXY();
}
}
但是编译不能通过:因此,泛型对于我们的方法重载是没有影响的!
于是我们又想到将fun函数这样改:不设置泛型
public static void fun(Point p)
{
p.getXY();
}
执行结果:
这样做,我们的确可以得到我们想要的结果,但是这回造成极大的安全隐患
public class Demo{
public static void main(String args[]){
Point <Integer> p1=new Point<>(10,20);
Point <String> p2=new Point<>("10","20");
fun(p1);
fun(p2);
}
public static void fun(Point p)
{
p.setXY(10.4,20.0);
p.getXY();
}
}
执行结果:
这样我们的p1和p2的x和y的值就有可能被修改为任意类型的值,这对我们的数据来说是不安全的。
因此,为了保障我们数据的安全性,又能进行多种泛型的引用,就需要用到通配符“ ?”
如以下程序:
public class Demo{
public static void main(String args[]){
Point <Integer> p1=new Point<>(10,20);
Point <String> p2=new Point<>("10","20");
fun(p1);
fun(p2);
}
public static void fun(Point<?> p)
{
p.getXY();
}
}
在通配符“ ?”的基础上还有两类小的通配符“?excends class”和“?super class”,
“?excends class”:用于设置泛型的上限,如:?extends Number 表示该泛型类型只允许设置Number以及其子类,在定义类的泛型时使用,比如以下程序:
class Point <T extends Number>
{
private T x;
private T y;
public Point(T x,T y){
this.x=x;
this.y=y;
}
public void setXY(T x,T y){
this.x=x;
this.y=y;
}
public void getXY(){
System.out.println("x="+this.x+" "+"y="+this.y);
}
}
“?super class”:用于设置泛型的下限,如?super String 表示该泛型类型只允许设置String及其父类,在使用类的泛型时使用。比如以下程序:
public static void fun(Point<? super String> p)
{
p.getXY();
}
对于通配符而言,一定要理解概念和定义,在日后的系统类库中经常会遇到通配符的使用。
三、泛型接口的两种实现形式
1、子类继续使用父类的泛型类型
interface Per <T>
{
public abstract T getMsg(T t);
}
class Student<S> implements Per<S>
{
public S getMsg(S t){
return t;
}
}
public class Demo{
public static void main(String args[]){
Per<String> s=new Student<String>();
System.out.println(s.getMsg("年货哦"));
}
}
2、子类在继承父接口时直接指定泛型类型
interface Per <T>
{
public abstract T getMsg(T t);
}
class Student implements Per<String>
{
public String getMsg(String t){
return t;
}
}
public class Demo{
public static void main(String args[]){
Per<String> s=new Student();
System.out.println(s.getMsg("你好呀"));
}
}
四、泛型方法
将泛型标记写在方法上的方法就就泛型方法,但是泛型方法不一定非要出现在泛型类之中。即一个类上没有定义泛型也可以使用泛型方法,这种泛型方法在我们的开发中很常见。
如以下程序:
ublic class Demo{
public static void main(String args[]){
String a[]=fun("1","2","3");
for(String temp : a)
System.out.println(temp);
}
public static <T> T[] fun(T... args){//泛型方法
return args;
}
}