文章目录
1.泛型
假设我们现在需要定义一个描述坐标的类Point,需要提供两个属性x,y,对于这两个属性内容可能会有如下选择:
- x=10、y=20 整形
- x=1.2、y=2.3 浮点型
- x=好好学习、y=天天向上 String
对于这种情况,属性x,y不能设置为具体的数据类型,我们可以将属性x,y定义为Object类
class Point{
private Object x;
private Object y;
public Point() {
}
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;
}
}
public class Test {
public static void main(String[] args) {
Point point = new Point();
//整形
point.setX(10);
point.setY(20);
int x1 = (Integer)point.getX();
int y1 = (Integer)point.getY();
System.out.println(x1+"、"+y1);
//浮点型
point.setX(1.2);
point.setY(2.1);
double x2 = (double)point.getX();
double y2 = (double)point.getY();
System.out.println(x2+"、"+y2);
//String类
point.setX("好好学习");
point.setY("天天向上");
String x3 = (String)point.getX();
String y3 = (String)point.getY();
System.out.println(x3+"、"+y3);
}
}
运行结果:
10、20
1.2、2.1
好好学习、天天向上
但是对于主方法是客户端的操作,用户并不知道x,y必须是两个相同的数据类型,如果出现以下情况:
point.setX(10);
point.setY("天天向上");
String x3 = (String)point.getX();
String y3 = (String)point.getY();
System.out.println(x3+"、"+y3);
整形一个具体的数据类型无法变成String引用数据类型,但是在编译期间不会报错,只有执行的时候才会出现类型转换异常(运行时异常),所以为了解决这种异常我们引出了泛型。
2.1 泛型类
泛型指的是:在类定义的时候并不会设置类中的属性或方法中参数的具体类型,而是在类使用的时候在定义
语法:
class Myclass<T>{
T value1;
}
T称为类型参数,用于指代任何类型,当然不一定用字母T来表示,任何东西都可以,但是为了规范起见,我们一般用如下字母:
- T:代表一般的类
- E:代表Element的意思,常用于泛型类中的属性
- K V 键值对
class Point<T>{
private T x;
private T y;
/**
* 这不叫泛型方法,只是返回值是一个泛型
* @return
*/
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
public class Test{
public static void main(String[] args) {
Point<Integer> point = new Point<>();
point.setX(10);
point.setY(20);
int x = point.getX();
int y = point.getY();
System.out.println(x+"、"+y);
Point<String> point1 = new Point<>();
point1.setX("好好学习");
point1.setY("天天向上");
String x1 = point1.getX();
String x2 = point1.getY();
System.out.println(x1+"、"+x2);
}
}
运行结果:
10、20
好好学习、天天向上
在这种情况下,我们在使用类时定义了属性的类型,如果输入的类型不是起初设置的类型,编译时期就会报错
泛型只允许引用数据类型,所有基本数据类型必须使用包装类
2.3 泛型方法
语法:
//<T> 占位符 表明此方法是泛型方法 返回值是void
public <T> void myMethod(T t){
System.out.println(t);
}
//<T> 占位符 表明此方法是泛型方法 返回值是T
public <T> T myMethod(T t){
return t;
}
class Myclass{
public <T> void print(T t){
System.out.println(t);
}
public <T> T print1(T t){
return t;
}
}
public class Test{
public static void main(String[] args) {
Myclass myClass = new Myclass();
myClass.print("hello");
myClass.print(12);
myClass.print(1.2);
System.out.println(myClass.print1("hello"));
System.out.println(myClass.print1(12));
System.out.println(myClass.print1(1.2));
}
}
运行结果:
hello
12
1.2
hello
12
1.2
当泛型类与泛型方法共存时,泛型类中的类型参数 与 泛型方法中的类型参数没有关系,泛型方法始终以自己定义的类型参数为准,为规范期间,泛型方法类型参数与泛型类的类型参数不要同名。
class Myclass<T>{
//泛型类的类型参数与属性的类型一致
private T t;
/**
* 此方法是一个普通方法
* 普通方法中的参数类型与泛型类的类型参数一致
* @param t
*/
public void metod(T t){
System.out.println(t);
}
/**
* 此方法是一个泛型方法<T>只是说明该方法是个泛型方法而已
* 泛型方法中的参数类型与泛型类的类型参数不一致
* 所以不建议两者名字相同
* @param t
* @param <E>
*/
public <E> void metod1(E t){
System.out.println(t);
}
}
public class Test{
public static void main(String[] args) {
Myclass<String> myClass = new Myclass<>();
//调用普通方法
myClass.metod("hello");
//调用泛型方法
myClass.metod1(12);
}
}
运行结果:
hello
12
2.3 通配符
class Myclass<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class Test{
public static void main(String[] args) {
Myclass<String> myClass = new Myclass<>();
myClass.setT("hello");
print(myClass);
}
/**
* 此方法的参数是个泛型
* @param myClass
*/
public static void print(Myclass<String> myClass){
System.out.println(myClass.getT());
}
}
上述的print方法参数是个泛型,此时若想打印其他类型的值,不仅要定义其他类型的泛型对象。还需要重载print方法,为方便起见引入通配符
2.3.1 ? 可以接收任意类型
class Myclass<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class Test{
public static void main(String[] args) {
Myclass<String> myClass = new Myclass<>();
myClass.setT("hello");
print(myClass);
Myclass<Integer> myClass1 = new Myclass<>();
myClass1.setT(123);
print(myClass1);
}
/**
* 此方法的参数是个泛型
* @param myClass
*/
public static void print(Myclass<?> myClass){
System.out.println(myClass.getT());
}
}
2.3.2 ? extends 类:设置/取得泛型上限
用在类上 :T extends 类--------T必须为类或者类的子类
用在方法上:只能接收类或者其子类的泛型类 只能取得类中的属性值,不能修改值(可能会发生父类到子类的向下转型,需要强转,由于子类不确定,无法转型)
? extends 类 Number :表示泛型必须是Number或其子类
class Myclass<T extends Number>{
private T t;
public T getT() { return t; }
public void setT(T t) { this.t = t; }
}
public class Test{
public static void main(String[] args) {
Myclass<String> myClass = new Myclass<>();
myClass.setT("hello");
print(myClass);
}
/**
* 此方法的参数是个泛型 必须接受Number类或其子类
* @param myClass
*/
public static void print(Myclass<? extends Number> myClass){
System.out.println(myClass.getT());
}
}
2.3.4 ?super 类 :取得泛型下限
只能用在方法中,只能接收 类或者 其父类的泛型类 可以设置属性值(子类到父类是自动的向上转型)
? super String:表示此方法只能取得String或者其父类
class Myclass<T>{
private T t;
public T getT() { return t; }
public void setT(T t) { this.t = t; }
}
public class Test{
public static void main(String[] args) {
Myclass<Object> myClass = new Myclass<>();
myClass.setT(2);
print(myClass);
}
/**
* 此方法的参数是个泛型
* 参数代表接收的类必须大于等于String
* @param myClass
*/
public static void print(Myclass<? super String> myClass){
System.out.println(myClass.getT());
}
}
2.4 泛型接口
语法:
interface IInterface<T>{
T test(T t);
}
实现:
class InterfaceImpl<T> implements IInterface<T> //子类也是泛型类 并且两个T一致
class InterfaceImpl implements IInterface<String> //子类定义时确定好类型
2.5 类型擦除(语法糖)
语法糖:尽存在于编译阶段,编译后就消失不见
泛型信息仅存在与编译阶段,进入JVM之前,与泛型相关的信息会被擦除调,换句话说,泛型类与普通类在Java虚拟机内没有任何区别
class MyClass<T>{
T t;
}
public class Test{
public static void main(String[] args) {
MyClass<String> class1 = new MyClass<>();
MyClass<Integer> class2 = new MyClass<>();
//getClass() 是一个反射 启动JVM虚拟机会返回对象的类型
System.out.println(class1.getClass()==class2.getClass());
}
}
结果为:true
泛型类进入JVM之前会进行类型擦除,之前泛型类的类型参数若没有指定上限,会被擦除称为Object类型,如果指定上限,则类型参数被替换为相应的类型上限
class MyClass<T,E extends Number>{
public T t;
public E e;
}
public class Test{
public static void main(String[] args) {
MyClass<String,Integer> class1 = new MyClass<>();
//取得Myclass类中的所有属性
Field[] fields = class1.getClass().getDeclaredFields();
for(Field field:fields){
//输出属性的类型
System.out.println(field.getType());
}
}
}
执行:
class java.lang.Object
class java.lang.Number