Java泛型

1.了解泛型

        了解泛型以及应用场景中不使用泛型出现的问题(通过定义类属性为Object)

        泛型是指再创建一个类时不指定类中属性的数据类型,而是在创建类对象时再指定相应的数据类型。

        例如我们需要创建一个xy坐标轴,由于不确定要传入什么数据类型的数据,创建两个Object属性接收数据,此时xy轴可以为任意类型。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Point {
    //x轴
    private Object x;
    //y轴
    private Object y;
}

 以传入 String 和 Integer 为例:

        //1.传入两个 String 类型
        Point point1 = new Point("x轴坐标", "y轴坐标");
        String x1 = (String) point1.getX();  //正常获取

        //2.传入两个 Integer 类型
        Point point2 = new Point(12, 13);
        Integer x2 = (Integer) point2.getX();

        //3.传入一个 Integer 类型,一个 String 类型
        Point point3 = new Point(12, "y轴坐标");
        Integer x3 = (Integer) point3.getX();
        String y3 = (String) point3.getY();

 虽然在需要使用此类时可以传入任意数据类型,此时问题就出现了,但当需要取出数据时需要进行类型强制转换,这大大影响了代码的质量和实际中的代码可观性,并且造成逻辑混乱。

        因此我们需要使用泛型来定义需要的数据类型。

2.使用泛型

2.1定义泛型类

定义格式:

public class 类名<泛型标识,泛型标识...>{
      public 泛型标志 属性名;

      public 泛型标志 属性名;

}

类中需要多少个参数在<>中写几个泛型。泛型标识符可以是任意字符,习惯使用 T 。

以我们此时需要两个为例,x 和 y。我们需要的 x y 数据类型相同,故因此只写一个泛型参数。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PointT<T> {
    //T为泛型标识符
    private T x;
    private T y;
}

当然也可以定义不同的数据类型参数 T , E ... 。比如:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PointTE<T,E> {
    //T , E 为泛型标识符
    private T x;
    private E y;
}

2.2实际应用

分别创建两个对象,一个泛型为String 一个为Integer 。

        //此时泛型为 String 
        PointT<String> stringPointT = new PointT<>("x轴坐标1", "y轴坐标2");
        System.out.println(stringPointT);
        //输出结果 PointT(x=x轴坐标1, y=y轴坐标2)

        //此时泛型为 Integer
        PointT<Integer> integerPointT = new PointT<>(12, 13);
        System.out.println(integerPointT);
        //输出结果 PointT(x=12, y=13)
        

 这是我们就不需要再将需要的数据进行强制类型转换了,直接将想要的数据类型填充在<>内,自动输出相应类型的数据。

如果定义为两个不同类型的泛型:

        //两个不同类型的泛型 String , Integer
        PointTE<String, Integer> stringIntegerPointTE = new PointTE<>("x轴坐标1",15);
        System.out.println(stringIntegerPointTE);
        //输出结果 PointTE(x=x轴坐标1, y=15)

3.通配符 ?

一般我们需要调用一个需要接收泛型类参数的方法时,只能根据方法形参传入对应的类和泛型相同的实参。定义一个泛型类 Info<T>,一个方法fun。

/**
 * 定义泛型类
 */
@Data
class Info<T> {
    private T var;
    
    public void show() {
        System.out.println("var===" + var);
    }
}

//不推荐的方法
    public static void fun(Info<String> info) {
        info.show();
    }

        //调用方法
        Info<Integer> info = new Info<>();
        info.setVar(14);
        //fun(info); //泛型要求为 String 因此无法调用

        Info<String> info1 = new Info<>();
        info1.setVar("通配符是?");
        fun(info1);  //泛型方法只接收相同类 相同泛型

每当此类需要一个不同的数据类型参数就要重新写一个对应的方法,这样无疑是提高了这个方法的局限性,因此可以用通配符 ? 来提高代码的复用性。定义方法fun1,使用 ? 代替 T 。

    //使用通配符的方法
    //能够让泛型接受任意泛型类型 必须使用通配符 ? 是泛型的通配符
    public static void fun1(Info<?> info) {
        info.show();
    }
        
        //泛型为 Integer
        Info<Integer> info = new Info<>();
        info.setVar(14);

        //泛型为 String
        Info<String> info1 = new Info<>();
        info1.setVar("通配符是?");

        //此时使用通配符可以接受任意泛型的类
        fun1(info);  
        fun1(info1);

4.受限泛型

我们在使用泛型传递操作中也可以为泛型的类型设定一个范围 ,

分为:  泛型上限 <? extends 类> 和 泛型下限 <? super 类>

定义格式:声明对象: 类名称< ?  extends / super  类> 对象名称;

                 定义类:  [访问权限] 类名称<泛型标识  extends / super  类>{ }

                 方法: [访问权限] 返回值类型 方法名( 类名称<泛型标识  extends / super  类>  形参名 ){ }

4.1 泛型上限 <? extends 类>

我们以类 Number 作为受限上限为例。

分别创建泛型为 Integer 、Object 、String 的对象解实说明。

        //泛型上限
        Info<Integer> info2 = new Info<>();
        fun2(info2);  // Integer 是 Number 子类 满足泛型上限
        
        Info<Object> info21 = new Info<>();
        Info<String> info22 = new Info<>();
        //fun2(info21);  // Object 为 Number 父类 超出泛型上限 泛型需要其为子类或Number
        //fun2(info22);  // String 不为 Number 类 不满足泛型上限 泛型需要其为子类或Number

    //泛型上限 <? extends 类>
    public static void fun2(Info<? extends Number> info) {
        info.show();
    }

4.2 泛型下限 <? super 类>

我们以类 Number 作为受限下限为例。

分别创建泛型为 Integer 、Object 、String 的对象解实说明。

        //泛型下限
        Info<Integer> info3 = new Info<>();
        //fun3(info3);  // Integer 为 Number 子类 不到泛型下限 泛型需要为其父类或Number 
        
        Info<Object> info31 = new Info<>();
        Info<String> info32 = new Info<>();
        fun3(info31);  // Object 为 Number 父类 满足泛型下限
        //fun3(info32);  // String 不为 Number 类 不满足泛型下限


    //泛型下限 <? super 类>
    public static void fun3(Info<? super Number> info) {
        info.show();
    }

总结:泛型上限为继承类的本身或其子类,泛型下限为super类本身或其父类。

5.泛型接口 (以 USB<T> 为例)

语法:public interface 接口名<泛型标识 , ...>{ }

创建一个接口,可以将接口设置为泛型。实现泛型接口有两种方式:

1. 类实现泛型接口时为其指定泛型类型 。

2. 类实现接口时同时也设置类为泛型类 泛型标识符要一致 。

/**
 * 4.泛型接口 USB<T>
 */
interface USB<T> {
    public abstract void use(T s);
}

//4.1 类实现泛型接口时为其指定泛型类型 Mouse
class Mouse implements USB<String> {
    
    @Override
    public void use(String s) {
        System.out.println("s===" + s);
    }
}

//4.2 类实现接口时同时也设置类为泛型类 泛型标识符要一致 Upan<T>
class Upan<T> implements USB<T> {
    
    @Override
    public void use(T s) {
        System.out.println("s===" + s);
    }
}

调用类方法:

        //4.1 类实现泛型接口时为其指定泛型类型
        Mouse mouse = new Mouse();
        mouse.use("鼠标"); // s===鼠标

        //4.2 类实现接口时同时也设置类为泛型类 泛型标识符要一致
        Upan<String> stringUpan = new Upan<String>();
        stringUpan.use("U盘"); // s===U盘

总结:两种实现方法都各有用处,根据实际应用场景进行选择。

6. 泛型方法

以上都是将整个类进行泛型化,而泛型方法则是与其所在类是否是泛型没有任何关系,所在类可以是泛型也可以不是。

我们也可以为方法的形参和返回值类型同时设置泛型,以方便开发中的需求。

语法:[访问权限] <泛型标识> 泛型标识 方法名称(泛型标识 参数名称){ }

定义泛型方法:

    /**
     * 5.泛型方法 fun4
     */
    public <T> T fun4(T a) {
        System.out.println("---------------"+a);
        return a;
    }

调用方法:

        PointTest pointText = new PointTest();
        Integer i = pointText.fun4(15);
        String s = pointText.fun4("15 String");

        //输出结果:
        //---------------15
        //---------------15 String

总结:泛型方法的灵活性与实用性更为广,能够在开发中提供很大的操作性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值