首页后端开发JAVAJava官方笔记8泛型

Java官方笔记8泛型

时间2023-10-22 20:04:02发布访客分类JAVA浏览1035
导读:泛型为什么需要泛型?generics enable types (classes and interfaces to be parameters when defining classes, interfaces and methods....

泛型

为什么需要泛型?generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods.

说白了就像Python动态语言的变量,是动态的,可以指向任意类型。

泛型有个好处是不需要类型转换:

List list = new ArrayList();
    
list.add("hello");
    
String s = (String) list.get(0);
    
ListString>
     list = new ArrayListString>
    ();
    
list.add("hello");
    
String s = list.get(0);
       // no cast

这个例子的泛型,是指List的实现使用了泛型T> ,从而给使用带来了好处。

定义

class nameT1, T2, ..., Tn>
 {
 /* ... */ }
    

比如定义class ListT> ,使用ListString>

将以下代码:

public class Box {
    
    private Object object;


    public void set(Object object) {
     this.object = object;
 }

    public Object get() {
     return object;
 }

}
    

改为泛型实现:

/**
 * Generic version of the Box class.
 * @param T>
     the type of the value being boxed
 */
public class BoxT>
 {
    
    // T stands for "Type"
    private T t;


    public void set(T t) {
     this.t = t;
 }

    public T get() {
     return t;
 }

}
    

T必须是非基本数据类型:any class type, any interface type, any array type, or even another type variable

type parameter命名采用单个大写字母:

  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S, U, V etc. - 2nd, 3rd, 4th types

这样能很好的跟其他命名区分开来。

使用泛型,必须要指定具体的值,比如这里的Integer:

BoxInteger>
     integerBox;
    

这就跟方法调用传参是一个道理。

Diamond,将:

BoxInteger>
     integerBox = new BoxInteger>
    ();
    

简写为:

BoxInteger>
     integerBox = new Box>
    ();
    

多个type parameters

public interface PairK, V>
 {
    
    public K getKey();
    
    public V getValue();

}
    

public class OrderedPairK, V>
     implements PairK, V>
 {
    

    private K key;
    
    private V value;


    public OrderedPair(K key, V value) {
    
    this.key = key;
    
    this.value = value;

    }


    public K getKey()    {
     return key;
 }

    public V getValue() {
     return value;
 }

}
    
PairString, Integer>
     p1 = new OrderedPairString, Integer>
    ("Even", 8);
      // 这里的int类型8,自动装箱为了I
PairString, String>
      p2 = new OrderedPairString, String>
    ("hello", "world");
    

简写为diamond:

OrderedPairString, Integer>
     p1 = new OrderedPair>
    ("Even", 8);
    
OrderedPairString, String>
      p2 = new OrderedPair>
    ("hello", "world");
    

嵌套:

OrderedPairString, BoxInteger>
    >
     p = new OrderedPair>
    ("primes", new BoxInteger>
    (...));
    

Raw Types

raw type is the name of a generic class or interface without any type arguments.

比如:

public class BoxT>
 {

    public void set(T t) {
 /* ... */ }

    // ...
}
    
BoxInteger>
     intBox = new Box>
    ();
    
Box rawBox = new Box();
  // 这个就是rawType

在IDEA有时候会碰到警告Raw use of parameterized class 'List',就是指的这个玩意。这是老式写法,raw types会绕过泛型的type checks,应该避免使用。

Generic Methods

定义,泛型位置在return type的前面:

public class Util {
    
    public static K, V>
     boolean compare(PairK, V>
     p1, PairK, V>
 p2) {
    
        return p1.getKey().equals(p2.getKey()) &
    &
    
               p1.getValue().equals(p2.getValue());

    }

}
    

public class PairK, V>
 {
    

    private K key;
    
    private V value;


    public Pair(K key, V value) {
    
        this.key = key;
    
        this.value = value;

    }


    public void setKey(K key) {
     this.key = key;
 }

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

    public K getKey()   {
     return key;
 }

    public V getValue() {
     return value;
 }

}
    

使用:

PairInteger, String>
     p1 = new Pair>
    (1, "apple");
    
PairInteger, String>
     p2 = new Pair>
    (2, "pear");
    
boolean same = Util.Integer, String>
    compare(p1, p2);
    

调用泛型方法时也可以省略泛型入参:

PairInteger, String>
     p1 = new Pair>
    (1, "apple");
    
PairInteger, String>
     p2 = new Pair>
    (2, "pear");
    
boolean same = Util.compare(p1, p2);
      // 这里省略了泛型入参

Bounded Type Parameters

有点像Python的typing,限制动态变量的类型,使用extends关键字:

public class BoxT>
 {
    

    private T t;
          

    public void set(T t) {
    
        this.t = t;

    }


    public T get() {
    
        return t;

    }
    

    public U extends Number>
 void inspect(U u){
    
        System.out.println("T: " + t.getClass().getName());
    
        System.out.println("U: " + u.getClass().getName());

    }


    public static void main(String[] args) {
    
        BoxInteger>
     integerBox = new BoxInteger>
    ();
    
        integerBox.set(new Integer(10));
    
        integerBox.inspect("some text");
 // error: this is still String!
    }

}
    

这样还能进一步调用bounded type parameters的方法:

public class NaturalNumberT extends Integer>
 {
    

    private T n;


    public NaturalNumber(T n)  {
     this.n = n;
 }


    public boolean isEven() {
    
        return n.intValue() % 2 == 0;
  // intValue()是Integer的方法
    }


    // ...
}

Multiple Bounds

Class A {
 /* ... */ }

interface B {
 /* ... */ }

interface C {
 /* ... */ }
    

class D T extends A &
     B &
     C>
 {
 /* ... */ }
    

class的位置必须在interface前面。

Bounded Type Parameters的用途之一,比如:

public static T>
 int countGreaterThan(T[] anArray, T elem) {
    
    int count = 0;
    
    for (T e : anArray)
        if (e >
     elem)  // compiler error
            ++count;
    
    return count;

}
    

会编译报错,因为> 符号只适用基本数据类型,如果想支持Object,怎么办呢:

public static T extends ComparableT>
    >
 int countGreaterThan(T[] anArray, T elem) {
    
    int count = 0;
    
    for (T e : anArray)
        if (e.compareTo(elem) >
     0)
            ++count;
    
    return count;

}
    

extends ComparableT> 以后调用compareTo()方法,就能既支持基本数据类型又能支持Object了。

泛型在继承时有个注意的点: BoxInteger>  and BoxDouble>  are not subtypes of BoxNumber>

正确的方式:

You can subtype a generic class or interface by extending or implementing it.

interface PayloadListE,P>
     extends ListE>
 {
    
  void setPayload(int index, P val);

  ...
}
    

Type Inference

类型推断:Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.(换个理解方式,就是动态变量需要知道绑定哪个类型)

Diamond就是一种Type Inference:

MapString, ListString>
    >
     myMap = new HashMap>
    ();
    

在构造方法中进行推断:

class MyClassX>
 {
    
  T>
 MyClass(T t) {

    // ...
  }

}
    
MyClassInteger>
     myObject = new MyClass>
    ("");
    

X推断为Integer,T推断为String。

Lambda Expressions也会根据上下文推断target type:

public static void printPersons(ListPerson>
     roster, CheckPerson tester)
public void printPersonsWithPredicate(ListPerson>
     roster, PredicatePerson>
     tester) 
printPersons(
        people, 
        p ->
     p.getGender() == Person.Sex.MALE
            &
    &
     p.getAge() >
    = 18
            &
    &
     p.getAge() = 25);
      // 自动推断为CheckPerson
printPersonsWithPredicate(
        people,
        p ->
     p.getGender() == Person.Sex.MALE
             &
    &
     p.getAge() >
    = 18
             &
    &
     p.getAge() = 25);
    )  // 自动推断PredicatePerson>
  • Variable declarations
  • Assignments
  • Return statements
  • Array initializers
  • Method or constructor arguments
  • Lambda expression bodies
  • Conditional expressions, ?:
  • Cast expressions

再看个例子:

public interface Runnable {
    
    void run();

}
    

public interface CallableV>
 {
    
    V call();

}
void invoke(Runnable r) {
    
    r.run();

}
    

T>
     T invoke(CallableT>
 c) {
    
    return c.call();

}
    
String s = invoke(() ->
     "done");
      // Lambda 

实际推断使用哪个?答案是Callable,因为它有return,而Runnable没有。

Wildcards

使用?extends表示上限:

List? extends Number>
    

可以是 ListInteger> ListDouble> , and ListNumber>

这里的extends既是class的extends,也是interface的implements。

ListObject> List?> 有什么区别?

①子类型

public static void printList(ListObject>
 list) {
    
    for (Object elem : list)
        System.out.println(elem + " ");
    
    System.out.println();

}
    

it prints only a list of Object instances; it cannot print ListInteger> ListString> ListDouble> , and so on, because they are not subtypes of ListObject> .

public static void printList(List?>
 list) {
    
    for (Object elem: list)
        System.out.print(elem + " ");
    
    System.out.println();

}
    

Because for any concrete type AListA>  is a subtype of List?> , you can use printList() to print a list of any type.

②值

You can insert an Object, or any subtype of Object, into a ListObject> . But you can only insert null into a List?> .

使用?super表示下限:

public static void addNumbers(List? super Integer>
 list) {
    
    for (int i = 1;
     i = 10;
 i++) {
    
        list.add(i);

    }

}
    

可以是ListInteger> ListNumber> , and ListObject>  — anything that can hold Integer values

?能支持集合子类型:

Type Erasure

Type Erasure是Java编译器为了实现泛型做的:

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety.
  • Generate bridge methods to preserve polymorphism in extended generic types.

Restriction on Generics

1、不能使用基本数据类型:

class PairK, V>
 {
    

    private K key;
    
    private V value;


    public Pair(K key, V value) {
    
        this.key = key;
    
        this.value = value;

    }


    // ...
}
    
Pairint, char>
     p = new Pair>
    (8, 'a');
      // compile-time error

只能使用包装类:

PairInteger, Character>
     p = new Pair>
    (8, 'a');
    

2、不能创建泛型实例:

public static E>
     void append(ListE>
 list) {
    
    E elem = new E();
      // compile-time error
    list.add(elem);

}
    

只能通过类来创建实例:

public static E>
     void append(ListE>
     list, ClassE>
 cls) throws Exception {
    
    E elem = cls.newInstance();
       // OK
    list.add(elem);

}
    
ListString>
     ls = new ArrayList>
    ();
    
append(ls, String.class);
    

3、static不能使用泛型

public class MobileDeviceT>
 {
    
    private static T os;
  // 在实例化后,会同时代表3种类型,显然不合理

    // ...
}
    
MobileDeviceSmartphone>
     phone = new MobileDevice>
    ();
    
MobileDevicePager>
     pager = new MobileDevice>
    ();
    
MobileDeviceTabletPC>
     pc = new MobileDevice>
    ();
    

4、不能instanceof

public static E>
     void rtti(ListE>
 list) {
    
    if (list instanceof ArrayListInteger>
) {
  // compile-time error
        // ...
    }

}
    

使用?可以:

public static void rtti(List?>
 list) {
    
    if (list instanceof ArrayList?>
) {
      // OK;
 instanceof requires a reifiable type
        // ...
    }

}
    

5、不能创建泛型数组:

ListInteger>
    [] arrayOfLists = new ListInteger>
    [2];
      // compile-time error

6、Cannot Create, Catch, or Throw Objects of Parameterized Types

// Extends Throwable indirectly
class MathExceptionT>
 extends Exception {
 /* ... */ }
        // compile-time error

// Extends Throwable directly
class QueueFullExceptionT>
 extends Throwable {
     /* ... */ // compile-time error
public static T extends Exception, J>
     void execute(ListJ>
 jobs) {

    try {

        for (J job : jobs)
            // ...
    }
 catch (T e) {
   // compile-time error
        // ...
    }

}
    

throws可以:

class ParserT extends Exception>
 {

    public void parse(File file) throws T {
     // OK
        // ...
    }

}

7、重载方法不能有擦除后相同的泛型:

public class Example {
    
    public void print(SetString>
 strSet) {
 }
    
    public void print(SetInteger>
 intSet) {
 }
  // 编译错误
}
    

参考资料: Generics https://dev.java/learn/generics/

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Java官方笔记8泛型
本文地址: https://pptw.com/jishu/506323.html
Java官方笔记9Lambda表达式 Java静态方法中引用非静态方法、变量报错处理:Non-static method ‘xxx()‘ cannot be referenced from a static context

游客 回复需填写必要信息