<返回更多

Java进阶知识,轻松理解Java泛型

2020-08-20    
加入收藏

在学习泛型之前我们先回顾下JAVA的数据类型以及涉及到的一些概念。

Java数据类型

Java的两大数据类型分为基础类型和引用类型。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。

Java进阶知识,轻松理解Java泛型

 

自动装箱

把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。

例如:

//自动装箱
Integer i=1;

而实际上编译器会调用static Integer valueOf(int i)这个方法,返回一个表示指定int值的Integer对象。Integer i=1; ->. Integer i=Integer.valueOf(1);

拆箱

跟自动装箱的方向相反,将引用类型转换为基本类型。

//拆箱
int i = new Integer(1);

自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

假如我们定义一个类来表示坐标,要求类中基础类型可以为整数 、小数、字符串,例如:

Object x=116,y=54;
Object x=116.92,y=54.31;
Object x="经度116.92",y="纬度54.31";

我们知道,基本数据类型可以自动装箱,被转换成对应的包装类。Object 是所有类的祖先类,任何一个类的实例都可以向上转型为 Object 类型,例如:

int -> Integer -> Object
double -> Double -> Object
String -> Object

泛型的使用

Java进阶知识,轻松理解Java泛型

 

如果要取出坐标值就需要向下转型,向下转型存在着风险,而且编译期间不容易发现,只有在运行期间才会抛出异常,所以要尽量避免使用向下转型。例如下面的实例:

public class Test {
    public static void main(String[] args){
        Point point = new Point();
        //int -> Integer -> Object
        point.setX(116);
        point.setY(54);
        //向下转型
        int x = (Integer) point.getX();
        int y = (Integer) point.getY();
        System.out.println("Point :x="+x+" y="+y);
        //double -> Double -> Object
        point.setX(116.92);
        point.setY("纬度54.32");
        //向下转型
        Double x1 = (Double) point.getX();
        //会抛出ClassCastException异常
        Double y1 = (Double) point.getY();
        System.out.println("Point :x1="+x1+" y1="+y1);
    }
}
class Point{
    Object x = null;
    Object y = null;
    public Object getX() {
        return x;
    }
    public void setX(Object x) {
        this.x = x;
    }
    public Object getY() {
        return y;
    }
    public void setY(Object y) {
        this.y = y;
    }
}

那么Java中如何避免这样的情况发生呢?

Java中泛型出现就是解决出现这样的问题,所谓的“泛型”就是任意的数据类型。以下代码将利用泛型解决上面的问题。

public class Test {
    public static void main(String[] args){
        //实例化泛型
        Point<Integer,Integer> point = new Point<Integer,Integer>();
        point.setX(116);
        point.setY(54);
        int x = point.getX();
        int y =  point.getY();
        System.out.println("Point :x="+x+" y="+y);
        Point<Double,String> point1 = new Point<Double,String>();
        point1.setX(116.92);
        point1.setY("纬度54.32");
        Double x1 =  point1.getX();
        String y1 = point1.getY();
        System.out.println("Point :x1="+x1+" y1="+y1);
    }
}
//定义泛型类
class Point<T1,T2>{
    T1 x = null;
    T2 y = null;
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
        this.y = y;
    }
    public T1 getX() {
        return x;
    }
    public void setX(T1 x) {
        this.x = x;
    }
}

实例中的T1和T2叫类型参数,类型参数是用来表示自定义标识符,用来传递数据的类型。Java中传值参数用小括号包围,泛型参数用尖括号包围,多个参数用逗号间隔,例如:

<T>或<T,E>

类型参数需要在类名后面给出。一旦给出了类型参数,就可以在类中使用了。类型参数必须是一个合法的标识符,习惯上使用单个大写字母,通常情况下,K 表示键,V 表示值,E 表示异常或错误,T 表示一般意义上的数据类型。

泛型类在实例化时必须指出具体的类型,也就是向类型参数传值,格式为:

className variable<dataType1, dataType2> = new className<dataType1, dataType2>();

也可以省略等号右边的数据类型,但是会产生警告,即:

className variable<dataType1, dataType2> = new className();

泛型的优点:使用泛型类时指明了数据类型,赋给其他类型的值会抛出异常,既不需要向下转型,也没有潜在的风险。

除了定义泛型类,还可以定义泛型接口和泛型方法,使用泛型方法时不必指明参数类型,编译器会根据传递的参数自动查找出具体的类型。

限制泛型的可用类型

通过 extends 关键字可以限制泛型的类型

public <T extends Number> T getMax(T array[]){
    T max = null;
    for(T element : array){
        max = element.doubleValue() > max.doubleValue() ? element : max;
    }
    return max;
}

<T extends Number> 表示 T 只接受 Number 及其子类,传入其他类型的数据会报错。这里的限定使用关键字 extends,后面可以是类也可以是接口。但这里的 extends 已经不是继承的含义了,应该理解为 T 是继承自 Number 类的类型。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>