java泛型的实现原理(java中的泛型指的是什么)
导语:Java进阶知识,轻松理解Java泛型
在学习泛型之前我们先回顾下Java的数据类型以及涉及到的一些概念。
Java数据类型
Java的两大数据类型分为基础类型和引用类型。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。
自动装箱
把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用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=,y=;
我们知道,基本数据类型可以自动装箱,被转换成对应的包装类。Object 是所有类的祖先类,任何一个类的实例都可以向上转型为 Object 类型,例如:
int -> Integer -> Objectdouble -> Double -> ObjectString -> Object
泛型的使用
如果要取出坐标值就需要向下转型,向下转型存在着风险,而且编译期间不容易发现,只有在运行期间才会抛出异常,所以要尽量避免使用向下转型。例如下面的实例:
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(+x++y); //double -> Double -> Object point.setX(116.92); point.setY(); //向下转型 Double x1 = (Double) point.getX(); //会抛出ClassCastException异常 Double y1 = (Double) point.getY(); System.out.println(+x1++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(+x++y); Point<Double,String> point1 = new Point<Double,String>(); point1.setX(116.92); point1.setY(); Double x1 = point1.getX(); String y1 = point1.getY(); System.out.println(+x1++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 类的类型。
本文内容由小苹整理编辑!