Java 泛型

导读:本篇文章讲解 Java 泛型,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

泛型的本质

Java泛型是JDK1.5引入的新特性,泛型提供了编译时类型安全检查。该机制允许程序在编译时检测到非法的类型。

泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个参数的类型就可以在使用时决定了,这种参数类型可以用在类、接口、方法上

Java 泛型

为什么需要泛型?

在Java推出泛型之前,可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用集合的过程中,程序员需要明确的知道存储每个元素的数据类型,不然很容易发生ClassCastException,泛型的好处是在编译期间进行类型检查,并且所有强制转化都是自动和隐式的。

  • 保证了类型的安全:编译器在编译期间就会做类型检查,告知是否插入错误的类型对象,使得程序更安全
  • 消除强制转换:没有设置泛型的类型默认为 Object,当我们获取元素时需要进行一个强转,当使用泛型时则不需要强转操作。
  • 避免不必要的装箱拆箱操作,提高程序的性能:非泛型时,将简单类型作为 Object 类型传递时会引起装箱和拆箱操作,这两个过程都会有极大的消耗
  • 提高代码的重用性

使用泛型

泛型有三种使用方式:泛型类、泛型接口和泛型方法

泛型类

  • 如果没有指定具体的数据类型,则默认是 Object。
  • 泛型的参数类型只能是类类型,不能是基本类型。
  • 泛型类在逻辑上可以看作是多个不同的类型,但实际上都是相同的类型
public class Generic<T> {
}

泛型类型必须是引用类型,即非基本数据类型,泛型类型可以有多个,每个参数使用逗号分隔

public class Generic<T> {

    private T key;

    public Generic() {
    }

    public Generic(T key) {
        this.key = key;
    }
    // get set toString...
}
Generic<Integer> generic = new Generic<>(123);
Generic<String> generic1 = new Generic<>("admin");
System.out.println(generic.getClass());
System.out.println(generic1.getClass());
System.out.println(generic.getClass() == generic1.getClass());

// conslog result
class Generic
class Generic
true

泛型类派生子类

创建子类先要创建父类对象,如果子类也是泛型类,那么子类的泛型标识就要和父类的泛型一致

//父类
public class Parent<E> {
    private E value;
    public E getValue() {
        return value;
    }
    public void setValue(E value) {
        this.value = value;
    }
}

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * @param <T>
 */
public class ChildFirst<T> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

若子类不是泛型类,那么父类就要明确泛型的数据类型

/**
 * 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
 */
public class ChildSecond extends Parent<Integer> {
    @Override
    public Integer getValue() {
        return super.getValue();
    }
    @Override
    public void setValue(Integer value) {
        super.setValue(value);
    }
}

泛型接口

public interface GenericInterface<T> {
    void show(T t);
    
    T getValue(T t);
}

实现类也是泛型类,实现类和接口的泛型类型要一致

/**
 * 泛型接口
 * @param <T>
 */
public interface Generator<T> {
    T getKey();
}
/**
 * 泛型接口的实现类,是一个泛型类,
 * 那么要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识
 * @param <T>
 * @param <E>
 */
public class Pair<T,E> implements Generator<T> {

    private T key;
    private E value;

    public Pair(T key, E value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public T getKey() {
        return key;
    }

    public E getValue() {
        return value;
    }
}

实现类不是泛型类,接口要明确数据类型

/**
 * 实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型。
 */
public class Apple implements Generator<String> {
    @Override
    public String getKey() {
        return "hello generic";
    }
}

泛型方法

泛型方法是在调用方法的时候指明泛型的具体类型。

// 只有 public 与 返回值之间有 <T> 才表示该方法为泛型方法,泛型类中使用的泛型成员方法并不是泛型方法
public <T> void showGeneric(T t){
}
  • 泛型方法能使方法独立于类而产生变化,该方法的泛型与类上面的泛型没有任何关系
  • 如果static方法要使用泛型能力,就必须使其成为泛型方法
     /**
     * 静态的泛型方法,采用多个泛型类型
     * @param t
     * @param e
     * @param k
     * @param <T>
     * @param <E>
     * @param <K>
     */
    public static <T,E,K> void printType(T t, E e, K k) {
        System.out.println(t + "\t" + t.getClass().getSimpleName());
        System.out.println(e + "\t" + e.getClass().getSimpleName());
        System.out.println(k + "\t" + k.getClass().getSimpleName());
    }

类型通配符

类型通配符一般是使用”?”代替具体的类型实参。所以,类型通配符是类型实参,而不是类型形参

类型通配符的上限

要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

要声明使用该类通配符, 采用的形式, 这里的E就是该泛型的上边界。

public class GrandFather {
}

public class Father extends GrandFather{
}

public class Son extends Father{
}

Java 泛型

类型通配符下限

要求泛型的类型只能是实参类型或者实参类型的父类型

Java 泛型

 

 类型擦除

        泛型是1.5引入的,在这之前是没有泛型的,但是泛型能够与之前的代码兼容,是因为泛型信息只处在编译阶段,在进入JVM之前,与泛型相关的信息都会被擦除。

无限制泛型擦除

Java 泛型

public class Generic<T> {
    private T key;
    // 省略 get set 等方法,下面同样
}
Class<Generic> clazz = Generic.class;
Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields).forEach(field -> {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
});

// conslog result 
key : Object

有限制类型擦除

Java 泛型

 

public class Generic<T extends Number> {
    private T key;
}
Class<Generic> clazz = Generic.class;
Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields).forEach(field -> {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
});

// conslog result 
key : Number

泛型方法

Java 泛型

 

桥接方法

Java 泛型

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95899.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!