Bootstrap

java泛型

1.介绍:

        Java泛型是一种可以在编译时进行类型检查的机制,它允许程序员编写灵活且类型安全的代码。以下是Java泛型的一些关键概念:

        

        1.泛型类

        泛型类可以有一个或多个类型参数,这些参数在类定义中被声明,并在实例化时被指定。例如,ArrayList<T>就是一个泛型类,其中T是一个类型参数,代表列表中存储的元素的类型。 

        2.泛型接口 :

        与泛型类类似,接口也可以被参数化。当实现一个泛型接口时,必须遵循接口中定义的类型参数。 

        3.泛型方法

        泛型方法是指在方法声明中包含类型参数的方法。这些方法独立于类的类型参数,可以属于普通类或泛型类。

        4.有界类型参数

        在泛型中使用extends关键字来限制类型参数的范围,这称为有界类型参数。例如,List<T extends Number>表示这个列表只能存储Number及其子类型的对象。

        5.通配符

        通配符?用于表示未知类型,它可以用作泛型的占位符。通配符有两种形式:无界通配符和有界通配符,分别用于不同的情况。

        6.类型擦除

        由于Java泛型是在编译时进行类型检查的,运行时并不保留类型信息,这个过程称为类型擦除。这意味着泛型类型信息在生成的字节码中不可见,因此无法在运行时通过反射获取到这些信息。

        7.PECS原则

        即Producer-Extends, Consumer-Super规则,它是使用通配符时的指导原则,帮助开发者正确地使用泛型来提高代码的可读性和安全性。

        8.泛型的使用场景

        泛型常用于集合类、自定义数据结构、算法的实现等场合,以提供更高的类型安全性和代码复用性。

总的来说,Java泛型是Java语言中一个强大的特性,它允许开发者编写更加灵活和健壮的代码,同时减少类型转换的需要,避免了潜在的类型转换错误

2.示例:

        1.泛型类

// 定义一个泛型类
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

// 实例化泛型类
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String item = stringBox.getItem(); // 类型为String

        2.泛型接口 :

// 定义一个泛型接口
public interface Comparator<T> {
    int compare(T o1, T o2);
}

// 实现泛型接口
public class StringComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
}

        3.泛型方法(可变参数)

public class Utility {
    // 定义一个泛型方法
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

     public static <E> void print(E... e) {  //泛型可变参数
        for (int i=0; i < e.length; i++) {
           System.out.println(e[i]);
        }
    }
}

// 调用泛型方法
Integer[] intArray = {1, 2, 3};
Utility.printArray(intArray); // 输出:1 2 3
Utility.print(1,5,"ttt");   // 输出:1 5 ttt




        4.有界类型参数

public class NumberList<T extends Number> {
    private List<T> list;

    public void add(T number) {
        list.add(number);
    }

    public T get(int index) {
        return list.get(index);
    }
}

NumberList<Integer> integerList = new NumberList<>();
integerList.add(1); // 正确,因为Integer是Number的子类

        5.通配符

// 使用无界通配符
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.print(item + " ");
    }
    System.out.println();
}

// 使用有界通配符
public static void printNumberList(List<? extends Number> list) {
    for (Number item : list) {
        System.out.print(item + " ");
    }
    System.out.println();
}

        6.PECS原则

                1.Producer-Extends(生产者-扩展):
// 定义一个泛型方法,接受一个List作为参数,并返回其中的元素
public static <T> T getFirst(List<? extends T> list) {
    return list.get(0);
}

// 调用该方法
List<Integer> integerList = Arrays.asList(1, 2, 3);
Integer firstElement = getFirst(integerList); // 正确,因为Integer是Number的子类

                在这个例子中,getFirst方法接受一个List<? extends T>类型的参数,表示这个列表可以存储任何T的子类型的对象。因此,我们可以将List<Integer>传递给这个方法,因为Integer是Number的子类。

                2.Consumer-Super(消费者-超类):
// 定义一个泛型方法,接受一个List作为参数,并将元素添加到另一个List中
public static <T> void addAll(List<? super T> destination, List<T> source) {
    for (T item : source) {
        destination.add(item);
    }
}

// 调用该方法
List<Object> objectList = new ArrayList<>();
List<String> stringList = Arrays.asList("A", "B", "C");
addAll(objectList, stringList); // 正确,因为String是Object的子类

                在这个例子中,addAll方法接受一个List<? super T>类型的参数,表示这个列表可以存储任何T的父类型的对象。因此,我们可以将List<String>传递给这个方法,因为String是Object的子类。

        通过遵循PECS原则,我们可以避免使用通配符时的类型转换错误,提高代码的可读性和类型安全性。

3.注意:

        1.没有提供泛型时默认为Object类型:

Generic<String> generic = new Generic<> ( key: "ABC");
String key = generic. getKey ();

//泛型类在创建对象的时候,没有指定类型,将按照0bject类型来操作。
Generic generic = new Generic ( key: "ABC");
Object key = generic. getKey ();

        2.java泛型不支持基础类型只支持类类型。

        3.泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型。

        4.泛型类派生子类:

                1.父类和子类都是泛型时,父类泛型要和子类泛型一样 (接口一样):
//泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。


//1
public class ChildFirst<T, E> extends Parent<T>{
    public T getValue() {
        return super. getValue() ;
    }
}

//2
public class ChildFirst<T> extends Parent<T>{
    public T getValue() {
        return super. getValue() ;
    }
}

//1和2都行
                2.父类是泛型而子类不是泛型时,父类泛型要明确 (接口一样):
//1
public class ChildSecond extends Parent{

    public Object getValue () {
        return super. getValue () ;
    }
}

//2
public class ChildSecond extends Parent<String>{

    public String getValue () {
        return super. getValue () ;
    }
}

;