好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

带你入门Java的泛型

泛型

1、简单泛型

泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。

泛型暂时不指定类型,在使用时决定具体使用什么类型。通过<T>来实现,T就是类型参数。

(1)元组

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class TwoTuple<A,B>{

     public final A first;

     public final B second;

     public TwoTuple(A a,B b){

         first = a;

         second = b;

     }

?

     @Override

     public String toString() {

         return "{ " + first +

                 ", " + second +

                 '}' ;

     }

}

(2)堆栈

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

class LinkedStack<T>{

     private class Node {

         T item;

         Node next;

         Node() { item = null ; next = null ; }

         Node(T item, Node next) {

             this .item = item;

             this .next = next;

         }

         boolean end() { return item == null && next == null ; }

     }

?

     private Node top = new Node();

     public void push(T item) { top = new Node(item, top); }

     public T pop() {

         T result = top.item;

         if (!top.end())

             top = top.next;

         return result;

     }

}

( 3 )RandomList

class RandomList<T>{

     private ArrayList<T> storage = new ArrayList<>();

     private Random rand = new Random( 47 );

     public void add(T item){

         storage.add(item);

     }

     public T select(){

         return storage.get(rand.nextInt(storage.size()));

     }

}

2、泛型接口

泛型也可以应用于接口,例如生成器,这是一种专门负责创建对象的类。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

import net.mindview.util.Generator;

import java.util.Iterator;

?

class Fibonacci implements Generator<Integer> {

     private int count = 0 ;

     public Integer next(){

         return fib(count++);

     }

     private int fib( int n){

         if (n< 2 ) return 1 ;

         return fib(n- 2 ) + fib(n- 1 );

     }

}

?

class IterableFibonacci implements Iterable<Integer> {

     private Fibonacci fib = new Fibonacci();

     private int n;

     public IterableFibonacci( int count){

         n = count;

     }

?

     @Override

     public Iterator<Integer> iterator() {

         return new Iterator<Integer>() {

             @Override

             public boolean hasNext() {

                 return n> 0 ;

             }

?

             @Override

             public Integer next() {

                 n--;

                 return fib.next();

             }

             public void remove() { // Not implemented

                 throw new UnsupportedOperationException();

             }

         };

     }

}

3、泛型方法

  泛型方法使得该方法能够独立于类而产生变化。使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型,这称为类型参数推断。

?

1

2

3

4

5

class GenericMethods{

      public <T> void f(T x){

       System.out.println(x.getClass().getSimpleName());

      }

}

(1)类型推断

  使用泛型有时候需要向程序中加入更多的代码。如下所示:

?

1

2

Map<Person,List<? extends Pet>> petPerson =

     new HashMap<Person,List<? extends Pet>>();

在泛型方法中可以通过类型推断来简化一部分工作。如下所示:

?

1

2

3

4

5

6

7

8

9

class New{

     public static <K,V> Map<K,V> map(){

         return new HashMap<K,V>();

     }

?

     public static void main(String[] args) {

         Map<Person,List<? extends Pet>> petPerson = New.map();

     }

}

类型推断只对赋值操作有效,其他时候并不起作用。如果将一个泛型方法的结果作为参数,传递给另一个方法时,另一个方法需要显式的类型说明。如下所示:

?

1

2

3

4

5

6

public class ExplicitTypeSpecification{

     static void f(Map<Person,List<? extends Pet>> petPerson){}

     public static void main(String[] args) {

         f(New.<Person,List<? extends Pet>>map());

     }

}

(2)通用的Generator

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import net.mindview.util.Generator;

?

public class BasicGenerator<T> implements Generator<T>{

     private Class<T> type;

     public BasicGenerator(Class<T> type){

         this .type = type;

     }

     public T next(){

         try {

             return type.newInstance();

         } catch (Exception e){

             throw new RuntimeException(e);

         }

     }

     public static <T> Generator<T> create(Class<T> type){

         return new BasicGenerator<T>(type);

     }

}

(3)Set实用工具实现数学方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class Sets{

     @SuppressWarnings ( "unchecked" )

     protected static <T> Set<T> copy(Set<T> s) {

         if (s instanceof EnumSet)

             return ((EnumSet)s).clone();

         return new HashSet<T>(s);

     }

?

     //并集

     public static <T> Set<T> union(Set<T> a, Set<T> b) {

         Set<T> result = copy(a);

         result.addAll(b);

         return result;

     }

     //交集

     public static <T> Set<T> intersection(Set<T> a, Set<T> b) {

         Set<T> result = copy(a);

         result.retainAll(b);

         return result;

     }

     //差集

     public static <T> Set<T> difference(Set<T> superset, Set<T> subset) {

         Set<T> result = copy(superset);

         result.removeAll(subset);

         return result;

     }

     //包含除了交集以外的所有元素

     public static <T> Set<T> complement(Set<T> a, Set<T> b) {

         return difference(union(a, b), intersection(a, b));

     }

}

4、擦除

Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此List<String>和List<Integer>在运行时事实上是相同的类型,都被擦除成它们的[原生]类型List。

(1)迁移兼容性

泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为他们的非泛型上界。擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这经常被称为[迁移兼容性]。

(2)擦除的问题

泛型的所有关于参数的类型信息都丢失了,所以不能用于显式地引用运行时类型的操作之中,例如转型、instanceof操作和new表达式。

5、擦除的补偿

(1)由于擦除原因,无法通过instanceof比较类型。如果引入类型标签,就可以转而使用动态的isInstance()。

?

1

2

3

4

5

6

7

8

9

public class ClassTypeCapture<T>{

     Class<T> kind;

     public ClassTypeCapture(Class<T> kind){

         this .kind = kind;

     }

     public boolean f(Object arg){

         return kind.isInstance(arg);

     }

}

(2)创建类型实例

通过工厂对象来创建实例。如果使用类型标签,就可以使用newInstance()来创建这个类型的新对象。

?

1

2

3

4

5

6

7

8

9

10

class ClassAsFactory<T>{

     T x;

     public ClassAsFactory(Class<T> kind){

         try {

             x = kind.newInstance();

         } catch (Exception e){

             throw new RuntimeException(e);

         }

     }

}

如果类没有默认的构造器,上面的案例会创建失败。为了解决这个问题,可以通过显示的工厂来实现。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

interface FactoryI<T>{

     T create();

}

class Foo2<T>{

     private T x;

     public <F extends FactoryI<T>> Foo2(F factory){

         x = factory.create();

     }

}

class IntegerFactory implements FactoryI<Integer>{

     public Integer create(){

         return new Integer( 6 );

     }

}

另一种方式是模板方法设计模式。

?

1

2

3

4

5

6

7

8

9

10

11

abstract class GenericWithCreate<T>{

     final T element;

     GenericWithCreate(){ element = create(); }

     abstract T create();

}

?

class X{}

?

class Creator extends GenericWithCreate<X>{

     X create(){ return new X(); }

}

(3)泛型数组

无法通过 T[] array = new T[sz] 来创建泛型数组,一般的解决方法是在需要泛型数组的地方都使用ArrayList。

在创建泛型数组时,有以下三种情况:

①创建时强制转型

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class GenericArray<T>{

     private T[] array;

     @SuppressWarnings ( "unchecked" )

     public GenericArray( int sz){

         array = (T[]) new Object[sz];

     }

     public T[] rep(){ return array; }

?

     public static void main(String[] args) {

         GenericArray<Integer> gai = new GenericArray<Integer>( 10 );

         Integer[] ia = gai.rep(); //引起ClassCastException

         Object[] oa = gai.rep();

     }

}

②方法返回时强制转型

?

1

2

3

4

5

6

7

8

9

10

11

12

13

class GenericArray2<T>{

     private Object[] array;

     @SuppressWarnings ( "unchecked" )

     public GenericArray( int sz){

         array = new Object[sz];

     }

     public T[] rep(){ return (T[])array; }

     public static void main(String[] args) {

         GenericArray<Integer> gai = new GenericArray<Integer>( 10 );

         Integer[] ia = gai.rep(); //引起ClassCastException

         Object[] oa = gai.rep();

     }

}

③使用Array.newInstance()

以上两种方法都无法创建具体类型的数组,无法推翻底层的数组类型,只能是Object[]。通过传入类型标记Class<T>,可以从擦除中恢复。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

class GenericArray3<T>{

     private T[] array;

     @SuppressWarnings ( "unchecked" )

     public GenericArray(Class<T> type, int sz){

         array = (T[]) Array.newInstance(type,sz);

     }

     public T[] rep(){ return array; }

     public static void main(String[] args) {

         GenericArray<Integer> gai = new GenericArray<Integer>(Integer. class , 10 );

         Integer[] ia = gai.rep(); //可以正常运行

         Object[] oa = gai.rep();

     }

}

6、边界

边界使得你可以在用于泛型的参数类型上设置限制条件,可以按照自己的边界类型来调用方法。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

public class Test {

     public static void main(String[] args) {

         Man m = new Man();

         m.hear();

         m.smell();

     }

}

?

interface SuperPower{}

interface SuperHearing extends SuperPower{

     void hearSubtleNoises();

}

interface SuperSmell extends SuperPower{

     void trackBySmell();

}

?

class SuperHero<POWER extends SuperPower>{

     POWER power;

     SuperHero(POWER power){ this .power = power; }

     POWER getPower(){ return power; }

}

?

class CaineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER>{

     CaineHero(POWER power){ super (power); }

     void hear(){ power.hearSubtleNoises(); }

     void smell(){ power.trackBySmell(); }

}

?

class SuperHearSmell implements SuperHearing,SuperSmell{

?

     @Override

     public void hearSubtleNoises() {

         System.out.println( "hearSubtleNoises" );

     }

?

     @Override

     public void trackBySmell() {

         System.out.println( "trackBySmell" );

     }

}

?

class Man extends CaineHero<SuperHearSmell>{

     Man(){ super ( new SuperHearSmell()); }

}

7、通配符

(1)List<? extends Fruit>协变

表示具有任何从Fruit继承的类型的列表。List<? extends Fruit>可以合法地指向一个List<Apple>。一旦执行这种类型的向上转型,就将丢失掉向其中传递任何对象的能力,甚至是传递Object也不行。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

List<? extends Fruit> flist =

     Arrays.asList( new Apple());

//Compile Error:can't add any type of object

//add()的参数是<? extends Fruit>,编译器不知道需要Fruit的哪个

//具体的子类型,因此不接受任何类型的Fruit

//flist.add(new Apple());

//flist.add(new Fruit());

//flist.add(new Object());

flist.add( null ); //Legal but uninteresting

Apple a = (Apple)flist.get( 0 ); //No warning

Fruit f = flist.get( 0 ); //No warning

flist.contains( new Apple()); //参数是Object

flist.indexOf( new Apple()); //参数是Object

(2)List<? super Fruit>逆变

超类型通配符可以安全地传递一个类型对象到泛型类型中。List<? super Fruit>意味着向其中添加Fruit或Fruit的子类型是安全的。

?

1

2

3

4

5

6

List<? super Fruit> flist = new ArrayList<Fruit>();

         flist.add( new Apple());

         flist.add( new Fruit());

//Error:Incompatible Type

//Fruit f = flist.get(0);

Object f = flist.get( 0 ); //OK,but type information has been lost

(3)无界通配符List<?>

List实际上表示[持有任何Object类型的原生List],List<?>表示[具有某种特定类型的非原生List,只是我们不知道那种类型是什么],List<? extends Object>表示[类型是Object的导出类]。

无界通配符的一个重要应用:处理多个泛型参数时,允许一个参数可以是任何类型,同时为其他参数确定某种特定类型。

?

1

2

Map<String,?> map = new HashMap<String,Integer>;

map = new HashMap<String,String>;

原生Holder与Holder<?>是大致相同的事物,但存在不同。它们会揭示相同的问题,但是后者将这些问题作为错误而不是警告报告。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

static void rawArgs(Holder holder,Object arg){

     //holder.set(arg);

     //Warning:Unchecked call to set(T) as member

     //of the raw type Holder

     //holder.set(new Wildcards());//Same Warning

     //Can't do this:don't have any 'T'

     //T t = holder.get();

     //OK,but type infomation has been lost

     Object obj = holder.get();

}

//Similar to rawArgs(),but errors instead of warnings

static void unboundedArg(Holder<?> holder,Object arg){

     //holder.set(arg);

     //Error:set(capture of ?) in Holder<capture of ?>

     //cannot be applied to (Object)

     //holder.set(new Wildcards());//Same Error

     //Can't do this:don't have any 'T'

     //T t = holder.get();

     //OK,but type infomation has been lost

     Object obj = holder.get();

}

(4)捕获转换

未指定的通配符类型被捕获,并被转换为确切类型。在f2()中调用f1(),参数类型在调用f2()的过程中被捕获,因此它可以在对f1()的调用中被使用。不能从f2()中返回T,因为T对于f2()来说是未知的。

?

1

2

3

4

5

6

7

8

static <T> void f1(Holder<T> holder){

     T t = holder.get();

      System.out.println(t.getClass().getSimpleName());

}

?

static <T> void f2(Holder<T> holder){

     f1(holder);

}

8、问题

(1)任何基本类型都不能作为类型参数

(2)实现参数化接口

一个类不能实现同一个泛型接口的两种变体。将泛型参数移除掉后,这段代码就可以正常编译了。

?

1

2

3

4

5

6

interface Payable<T>{}

?

class Employee implements Payable<Employee>{}

?

//Compile Error:cannot be inherited with different type arguments

class Hourly extends Employee implements Payable<Hourly>{}

(3)转型和警告

使用带有泛型类型参数的转型或instanceof不会有任何效果。

由于擦除原因,编译器无法知道这个转型是否安全,并且pop()方法实际上并没有执行任何转型。如果没有@SuppressWarnings注解,编译器将对pop()产生[Unchecked cast]警告。

?

1

2

3

4

private int index = 0 ;

private Object[] storage;

@SuppressWarnings ( "unchecked" )

public T pop(){ return (T)storage[--index]; }

(4)重载

由于擦除的原因,重载方法将产生相同的类型签名,导致程序不能编译。

?

1

2

3

4

public class UseList<W,T>{

      void f(List<T> v){}

      void f(List<W> v){}

  }

(5)基类劫持了接口

一旦为Comparable确定了ComparablePet参数,那么其他任何实现类都不能与ComparablePet之外的任何对象比较。在前面的[实现参数化接口]章节里面的第一个例子,就体现了基类劫持接口。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class ComparablePet

implements Comparable<ComparablePet> {

   public int compareTo(ComparablePet arg) {

       return 0 ;

   }

}

?

class Cat extends ComparablePet implements Comparable<Cat>{

   // Error: Comparable cannot be inherited with

   // different arguments: <Cat> and <Pet>

   public int compareTo(Cat arg) { return 0 ; }

} ///:~

?

class Hamster extends ComparablePet

     implements Comparable<ComparablePet>{

     public int compareTo(ComparablePet arg) {

         return 0 ;

     }

}

9、自限定

class Subtype extends BasicHolder<Subtype> {}这样用,就构成自限定了。从定义上来说,它继承的父类的类型参数是它自己。

从使用上来说,Subtype对象本身的类型是Subtype,且Subtype对象继承而来的成员(element)、方法的形参(set方法)、方法的返回值(get方法)也是Subtype了(这就是自限定的重要作用)。这样Subtype对象就只允许和Subtype对象(而不是别的类型的对象)交互了。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

class BasicHolder<T> {

     T element;

     void set(T arg) { element = arg; }

     T get() { return element; }

     void f() {

         System.out.println(element.getClass().getSimpleName());

     }

}

?

class Subtype extends BasicHolder<Subtype> {}

?

public class CRGWithBasicHolder {

     public static void main(String[] args) {

         Subtype st1 = new Subtype(), st2 = new Subtype(), st3 = new Subtype();

         st1.set(st2);

         st2.set(st3);

         Subtype st4 = st1.get().get();

         st1.f();

     }

} /* Output:

Subtype

*/

10、异常

由于擦除原因,将泛型应用于异常是非常受限的。但是,类型参数可能会在一个方法的throws子句中用到,这使得你可以编写随检查型异常的类型而发生变化的泛型代码。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

interface

Processor<T,E extends Exception> {

     void process(List<T> resultCollector) throws E;

}

?

class

ProcessRunner<T,E extends Exception>

         extends ArrayList<Processor<T,E>> {

     List<T> processAll() throws E {

         List<T> resultCollector = new ArrayList<T>();

         for (Processor<T,E> processor : this )

             processor.process(resultCollector);

         return resultCollector;

     }

}

?

class Failure extends Exception {}

?

class Processor1 implements

         Processor<String,Failure> {

     static int count = 3 ;

     public void process(List<String> resultCollector)

             throws Failure1_1, Failure1_2 {

         if (count-- > 1 )

             resultCollector.add( "Hep!" );

         else

             resultCollector.add( "Ho!" );

         if (count < 0 )

                 throw new Failure1();

     }

}

?

public class Test {

     public static void main(String[] args) {

         ProcessRunner<String,Failure> runner =

                 new ProcessRunner<String,Failure>();

         for ( int i = 0 ; i < 3 ; i++)

             runner.add( new Processor1());

         try {

             System.out.println(runner.processAll());

         } catch (Failure e) {

             System.out.println(e);

         }

     }

}

总结

本篇文章就到这里了,希望能给您带来帮助,也希望您能够多多关注的更多内容!

原文链接:https://HdhCmsTestcnblogs测试数据/yqsumAll/p/14953199.html

查看更多关于带你入门Java的泛型的详细内容...

  阅读:25次