我是靠谱客的博主 迷你吐司,这篇文章主要介绍Java编程思想读书笔记——泛型(三),现在分享给大家,希望可以做个参考。

15.11 问题

15.11.1 任何基本类型都不能作为类型参数

对于基本类型,只能使用对应的包装类来作为类型参数。这又涉及到自动装箱和自动拆箱的问题,会对性能造成一定的影响。
Org.apache.commons.collectiions.primitives这一开源的框架中可以使用由基本类型作为类型参数的泛型。
自动装箱机制不能用于数组。
例如:

复制代码
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
package com.mzm.chapter15; import com.mzm.chapter16.RandomGenerator; /** * 自动包装机制不能解决所有问题 * */ public class PrimitiveGenericTest { public static void main(String[] args) { String[] strings = FArray.fill(new String[7], new RandomGenerator.String(10)); for(String s : strings) { System.out.println(s); } Integer[] integers = FArray.fill(new Integer[7], new RandomGenerator.Integer()); for(int i : integers) { System.out.println(i); } //无法使用自动包装机制 //int[] b = FArray.fill(new int[7], new RandomGenerator.Integer()) } } class FArray { public static <T> T[] fill(T[] a, Generator<T> gen) { for(int i = 0; i < a.length; i++) { a[i] = gen.next(); } return a; } }
15.11.2 实现参数化接口

一个类不能实现同一个泛型接口的两种变体,因为存在擦除。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mzm.chapter15; /** * */ public class MutipleInterfaceVariants { } interface Payable<T> { } class Employee2 implements Payable<Employee2> { } //该类不能实现同一个泛型接口的不同类型参数版本 /*class Hourly extends Employee2 implements Payable<Hourly> { }*/
15.11.3 转型和警告

对带有泛型类型参数的变量进行转型或者使用instanceof,不会有任何结果。

复制代码
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
package com.mzm.chapter15; /** * */ public class GenericCast { public static final int SIZE = 10; public static void main(String[] args) { FixedSizeStack<String> strings = new FixedSizeStack<>(SIZE); for(String s : "A B C D E F G H I J".split(" ")) { strings.push(s); } for(int i = 0; i < SIZE; i++) { String s = strings.pop(); System.out.print(s + " "); } } } class FixedSizeStack<T> { private int index = 0; private Object[] storage; public FixedSizeStack(int size) { storage = new Object[size]; } public void push(T item) { storage[index++] = item; } /** * 由于擦除的存在,类型信息缺失,转型安全性未知 * @return */ @SuppressWarnings("unchecked") public T pop() { return (T)storage[--index]; } }

但有时泛型也确实有对转型的需要:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mzm.chapter15; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.util.List; /** * */ public class NeedCasting { //@SuppressWarnings("unchecked") public void f(String[] args) throws Exception { ObjectInputStream in = new ObjectInputStream(new FileInputStream(args[0])); List<Widget> shapes = (List<Widget>)in.readObject(); } }

注释@SuppressWarnings(“unchecked”),执行编译:

复制代码
1
2
注: NeedCasting.java使用了未经检查或不安全的操作。 注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

如果执行javac -Xlint:unchecked NeedCasting.java,又会出现以下情况:

复制代码
1
2
3
4
5
6
7
NeedCasting.java:13: 警告: [unchecked] 未经检查的转换 List<Widget> shapes = (List<Widget>)in.readObject(); ^ 需要: List<String> 找到: Object 1 个警告

这样就造成既要求转型,又告知不应转型的情况。
解决这样的问题,必须使用JavaSE 5中引入的新的转型形式。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mzm.chapter15; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.util.List; /** * */ public class ClassCasting { @SuppressWarnings("unchecked") public void f(String[] args) throws Exception { ObjectInputStream in = new ObjectInputStream(new FileInputStream(args[0])); List<Widget> lw2 = List.class.cast(in.readObject()); } }
15.11.4 重载

这种形式的重载是不允许的,因为在擦除之后,出现了两个同样的方法。这一错误,可通过编译器看出。

复制代码
1
2
3
4
5
6
7
8
9
10
11
package com.mzm.chapter15; import java.util.List; /** * */ public class UseList<W, T> { void f1(List<T> v) { } /*void f1(List<W> v) { }*/ }
15.11.5 基类劫持了接口

类型参数中的类型是确定的,不允许是其子类。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mzm.chapter15; /** * Created by 蒙卓明 on 2018/1/1. */ public class ComparablePet implements Comparable<ComparablePet> { @Override public int compareTo(ComparablePet o) { return 0; } } /* class Cat extends ComparablePet implements Comparable<Cat> { }*/
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.mzm.chapter15; /** * */ public class RestrictedComparablePets { } /** * Hamster虽然是ComparablePet的子类,但当其需要实现Comparable接口时,其类型参数也必须要是ComparablePet * 等价于Hamster是ComparablePet的子类即可 */ class Hamster extends ComparablePet implements Comparable<ComparablePet> { public int compareTo(ComparablePet arg) { return 0; } } class Gecko extends ComparablePet { public int compareTo(ComparablePet arg) { return 0; } }

15.12 自限定的类型

15.12.1 古怪的循环泛型

古怪的循环泛型(CRG)是指创建一个新类,其继承自一个泛型类型,接受自身类名作为泛型参数。

复制代码
1
2
3
4
5
6
7
8
9
package com.mzm.chapter15; /** * 创建一个新类,其继承自一个泛型类型,接受自身类名作为泛型参数 * */ public class CuriouslyRecurringGeneric extends GenericType<CuriouslyRecurringGeneric>{ } class GenericType<T> { }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mzm.chapter15; /** * */ public class CRGWithBasicHolder { public static void main(String[] args) { Subtype st1 = new Subtype(), st2 = new Subtype(); //新类SubType接受的参数和返回的值具有SubType类型,而不仅是基类BasicHolder类型 st1.set(st2); Subtype st3 = st1.get(); st1.f(); } } class Subtype extends BasicHolder<Subtype> { }

CRG的本质:父类用子类替代其参数。

15.12.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
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.mzm.chapter15; /** * 自限定 * */ public class SelfBounding { public static void main(String[] args) { A a = new A(); a.set(new A()); a = a.set(new A()).get(); a = a.get(); C c = new C(); c = c.setAndGet(new C()); } } /** * 自限定,泛型类以自身为泛型参数的边界 * @param <T> */ class SelfBounded<T extends SelfBounded<T>> { T element; SelfBounded<T> set(T arg) { element = arg; return this; } T get() { return element; } } /** * 自限定主要是,在继承关系中,将子类作为父类(泛型类)的泛型参数 */ class A extends SelfBounded<A> { } /** * 也可以使用其他参数 */ class B extends SelfBounded<A> { } class C extends SelfBounded<C> { C setAndGet(C arg) { set(arg); return get(); } } class D { } /* * 但是这一泛型参数必须是泛型类的子类才可 class E extends SelfBounded<D> { }*/ class F extends SelfBounded { }

自限定主要用于,定义一个泛型类的子类时,强制使用泛型类的子类作为其泛型参数(可以是自身或其他子类)。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mzm.chapter15; /** * 自限定用于泛型方法 * */ public class SelfBoundingMethods { static <T extends SelfBounded<T>> T f(T arg) { return arg.set(arg).get(); } public static void main(String[] args) { A a = f(new A()); } }
15.12.3 参数协变

自限定类型可以产生协变参数类型——方法参数类型会随子类而变化。
方法的返回类型随子类而变化:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.mzm.chapter15; /** * 非自限定情况 */ public class CovariantReturnTypes { void test(DerivedGetter d) { Derived d2 = d.get(); } } class Base { } class Derived extends Base { } interface OrdinaryGetter { Base get(); } interface DerivedGetter extends OrdinaryGetter { /** * 非自限定情况通过显式重写父类的get()方法,返回子类类型 * @return 子类类型 */ Derived get(); }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.mzm.chapter15; /** * */ public class GenericAndReturnTypes { void test(Getter g) { //子类Getter对象的返回类型为Getter Getter result = g.get(); GenericGetter gg = g.get(); } } /** * 自限定接口 * @param <T> 泛型类自身作为泛型参数的边界 */ interface GenericGetter<T extends GenericGetter<T>> { T get(); } /** * 自限定情况,无需通过显式覆盖同名方法实现返回类型随子类变化,实际上已经完成方法的重写 */ interface Getter extends GenericGetter<Getter> { }

方法的参数类型随子类而变化:

复制代码
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
package com.mzm.chapter15; /** * 非自限定情况 * Created by 蒙卓明 on 2018/1/6. */ public class OrdinaryArguments { public static void main(String[] args) { Base base = new Base(); Derived derived = new Derived(); DerivedSetter ds = new DerivedSetter(); //在DerivedSetter类中,两个set()方法共存,说明出现重载 ds.set(derived); ds.set(base); } } class OrdinarySetter { /** * 父类的set()方法 * @param base 以Base类自身作为参数类型 */ void set(Base base) { System.out.println("OrdinarySetter.set(Base)"); } } class DerivedSetter extends OrdinarySetter { /** * 非限定情况只能显式重载父类的set()方法,来实现方法参数类型随子类而变化 * @param derived 以Base子类Derived类型作为参数类型 */ void set(Derived derived) { System.out.println("DerivedSetter.set(Derived)"); } }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.mzm.chapter15; /** * 自限定情况 * Created by 蒙卓明 on 2018/1/6. */ public class SelfBoundedSetterAndCovariantArguments { void testA(Setter s1, Setter s2, SelfBoundSetter sbs) { //接受自身类型变量 s1.set(s2); //不接受父类SelfBoundSetter类型变量作为参数,只接受子类类型 //s2.set(sbs); } } interface SelfBoundSetter<T extends SelfBoundSetter<T>> { void set(T arg); } /** * 自限定情况,则无需显式重载父类同名方法,但是实际上父类的同名方法已经被覆盖 */ interface Setter extends SelfBoundSetter<Setter> { }
复制代码
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
package com.mzm.chapter15; /** * */ public class PlainGenericInheritance { public static void main(String[] args) { Base base = new Base(); Derived derived = new Derived(); DerivedGS dgs = new DerivedGS(); //同时接受父类对象和子类对象作为方法参数,说明父类和子类的set()共存 dgs.set(derived); dgs.set(base); } } /** * 非自限定 * @param <T> */ class GenericSetter<T> { void set(T arg) { System.out.println("GenericSetter.set(Base)"); } } class DerivedGS extends GenericSetter<Base> { /** * 非自限定只能显式重载父类的同名方法来实现方法参数类型随子类而变化 * @param derived */ void set(Derived derived) { System.out.println("DerivedGS.set(Derived)"); } }

15.13 动态类型安全

在向Java SE5之前的代码传递泛型容器时,旧代码可能会破坏泛型容器。Java SE5的java.util.Collections中有一组便利工具,可以解决在这种情况下类型检查问题。它们是静态方法:

复制代码
1
2
3
4
5
6
checkedCollection(); checkedList(); checkedMap(); checkedSet(); checkedSortedMap(); checkedSortSet();

这些方法接受两个参数,第一个是容器对象,第二个是检查的类型。

复制代码
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
package com.mzm.chapter15; import com.mzm.chapter14.Cat; import com.mzm.chapter14.Dog; import com.mzm.chapter14.Pet; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * */ public class CheckedList { @SuppressWarnings("unchecked") static void oldStyleMethod(List probablyDogs) { probablyDogs.add(new Cat()); } public static void main(String[] args) { List<Dog> dogs1 = new ArrayList<>(); //针对dogs1,插入Cat类型对象没有问题 oldStyleMethod(dogs1); List<Dog> dogs2 = Collections.checkedList(new ArrayList<>(), Dog.class); try { //针对dogs2,插入Cat类型对象抛出异常 oldStyleMethod(dogs2); } catch (Exception e) { System.out.println(e); } //以父类作为检查类型,也没有问题 List<Pet> pets = Collections.checkedList(new ArrayList<>(), Pet.class); pets.add(new Dog()); pets.add(new Cat()); } }

15.14 异常

由于擦除的存在,catch语句不能捕获泛型类型的异常,而泛型类也不能直接或间接地继承自Throwable。
但是类型参数可以应用于方法的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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.mzm.chapter15; import java.util.ArrayList; import java.util.List; /** * */ public class ThrowGenericException { public static void main(String[] args) { ProcessRunner<String, Failure1> runner = new ProcessRunner<>(); for(int i = 0; i < 3; i++) { runner.add(new Processor1()); } try { System.out.println(runner.processAll()); } catch (Failure1 e) { System.out.println(e); } ProcessRunner<Integer, Failure2> runner2 = new ProcessRunner<>(); for(int i = 0; i < 3; i++) { runner2.add(new Processor2()); } try { System.out.println(runner2.processAll()); } catch (Failure2 e) { System.out.println(e); } } } /** * 该接口执行process()方法 * @param <T> List中持有的对象类型 * @param <E> 抛出的异常类型 */ interface Processor<T, E extends Exception> { void process(List<T> resultCollector) throws E; } class ProcessRunner<T, E extends Exception> extends ArrayList<Processor<T, E>> { /** * 执行每一个存储在该容器中的Processor对象的process()方法 * @return * @throws E */ List<T> processAll() throws E { List<T> resultCollector = new ArrayList<>(); for(Processor<T, E> processor : this) { processor.process(resultCollector); } return resultCollector; } } class Failure1 extends Exception { } class Processor1 implements Processor<String, Failure1> { static int count = 3; @Override public void process(List<String> resultCollector) throws Failure1 { if(count-- > 1) { resultCollector.add("Hep!"); } else { resultCollector.add("Ho!"); } if(count < 0) { throw new Failure1(); } } } class Failure2 extends Exception { } class Processor2 implements Processor<Integer, Failure2> { static int count = 2; @Override public void process(List<Integer> resultCollector) throws Failure2 { if(count-- == 0) { resultCollector.add(47); } else { resultCollector.add(11); } if(count < 0) { throw new Failure2(); } } }

最后

以上就是迷你吐司最近收集整理的关于Java编程思想读书笔记——泛型(三)的全部内容,更多相关Java编程思想读书笔记——泛型(三)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部