Java泛型机制

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
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值