Java中泛型的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24477797/article/details/80819004

本篇讲解泛型,虽然之前也一直使用泛型,但理解的不够透彻。今天详细归纳下泛型的表示和使用场景,同时将平时一些没注意的地方,使用泛型来转变。

概念篇:

有三种使用方式,分别为:泛型类、泛型接口、泛型方法

泛型类

    1、定义方式:

public class Test<T>{
    private T value;

    public Test(){
    }

    public Test(T value){
        this.value = value;
    }

    public void setValue(T value){
        this.value = value;
    }

    public T getValue(){
        return value;
    }
}

    说明:

     ① 泛型申明需要菱形运算符,在类中使用需要在类名后<>申明,<>可以有多个泛型,以","分割,多个的情况可以参考 java.util.Map。

      ② T可以修改为任意标识(大小写英文字母都可以),但一般都是用大写英文字母,常见如:T、E、K、V等

    2、使用(以上方Test为例)

        ① 在泛型类中,当我们不使用到其中的泛型时,可以无视泛型,正常使用该类即可。

        ② 当我们使用到泛型时,变量申明可以带泛型也可以不带,可以理解成继承关系,使用父类型来申明(但注意实际中和继承关系是两码事,甚至需要注意两者的区别,区别在下文会提到)。如:

Test test1 = new Test<>("test");
Test<String> test2 = new Test<>("test");

        ③ 在使用到泛型时则需要带上<省略>或者<T>,不带的话编译器会有警告,如下方new对象时使用到了泛型:

Test test1 = new Test<>("test");
Test test2 = new Test<String>("test");

        警告的原因在于确保我们在使用到泛型时,类型是正确的。如在变量申明时不带泛型,直接调用setValue("")则会有警告,编译器提示我们使用的类型是否正确。

泛型接口

    1、定义:

    在定义上同泛型类

public interface Test<T>{
    T next();
}

    2、特殊点:

        1)接口被接口继承,同时需要继承泛型:

public interface Test2<T> extends Test<T>{
}

        2)接口被实现(抽象类被实现也一样)。可以实现泛型,也可以不实现,不实现则以Object。如下为实现和不实现的代码:

public class Test3 implements Test<String>{
    @Override
    public String next(){
        return null;
    }
}
public class Test3 implements Test{
    @Override
    public Object next(){
        return null;
    }
}

泛型方法

    1、定义

    泛型方法,需要方法自己定义泛型,其生命周期是在于方法使用期间,定义格式是在方法名之前使用菱形运算符定义<>,如下:

publc <T> void getValue(T value){
}

    2、注意:

        ① 注意泛型方法与泛型类下的普通方法的区分。单纯使用在class上定义的泛型的方法,不算泛型方法。当然泛型方法也就一个称号。

        ② 静态方法不能直接使用类上的泛型。可以这么理解:泛型的使用会伴随着对象,静态方法不能访问该类生成对象的属性。

通配符

    1、定义:

    通配符通常用来表示对已经实例化的对象,忽略其使用的泛型的一种统一概括。一般都是作为引用表示。格式:Class<?>

    例如:

public void setList(List<?> lists){
}

    2、过滤:

    <?> 表示是所有情况,我们也可以将? 缩小点范围。例如: <? extends xxx> 泛型范围缩小为继承于xxx类的所有类。

    3、注意:需要注意的是与泛型<T>及Object的区别

        1)与泛型<T>的区别

        与泛型<T>相同,都是用来表示所有泛型里面的某一种。但泛型<T>需要跟随申明,让编译器明白具体使用的类型。通配符不需要申明,一般都是用来作为引用,只能读取,不能修改。

        官方点的话:在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能向其中添加元素, 因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL

        2)与Object的区别

        尽管? 或者 T 的使用一般都是使用Object的衍生类。不同之处:

public void setList(List<Object> lists){
}

private void test(){
    List<String> strList = new ArrayList<>();
    List<?> list  = new ArrayList<>();    
    // 错误1:setList(java.util.List<java.lang.Object>) in xxx cannot be applied to ( java.util.List<java.lang.String>)
    setList(strList);
    // 错误2:setList(java.util.List<java.lang.Object>) in xxx cannot be applied to ( java.util.List<capture<?>>)
    setList(list);
}

        可以看出通配符<?>,指定的范围比<Object>广,它还可以表示一种关系,如上方提到的继承关系下的所有类。

使用篇

使用泛型可以给我们的程序提高一定的扩展性。举例说明,下方几种情况我们可以修改为泛型的表示方式:

    1)在不清楚引入的类型时,常常就简单了使用Object来定义解决,使用时通过instanceof来判断。这里我们就可以转换下思维,用上泛型。

    2)规范化代码。在设计抽象类时,我们希望一个类能够被指定多种类型中的一种类型使用,在使用时又不用担心类型转换异常等问题。就像List,当定义List<String> 时,之后的该List只能使用String,借助编译器,不怕该List中还保存着Integer等类型。

    3)在实际开发中,我们很多情况下想确保继承于一个自定义的抽象(可以理解为框架),而非仅Object,减少后续使用该类的开发者阅读源码的时间,也算也是规范化代码。如下方例子,这里指定为 extends RecyclerView.ViewHolder:

public abstract class FrameAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {
}

暂时仅想到这三条,后续想到合适的地方我再补上。

本篇泛型博客,都是结合网上说法及自身理解所归纳的,不敢确保完全正确,希望大家看到有问题的地方能够及时指正,后续我也会不断改正。

猜你喜欢

转载自blog.csdn.net/qq_24477797/article/details/80819004