泛型(13):动态类型安全、异常
一、动态类型安全
因为可以向java SE5之前的代码传递泛型容器,所以旧式代码仍旧有可能会破坏你的容器,java SE5的java.util.Collections中有一组便利工具,可以解决在这种情况下的类型检查问题,它们是:静态方法checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()和checkedSortedSet()。这些方法每一个都会将你希望动态检查的容器当作第一个参数接受,并将你希望强制要求的类型作为第二个参数接受。
受检查的容器在你试图插入类型不正确的对象时抛出ClassCastException,这与泛型之前的(原生)容器形成了对比,对于后者来说,当你将对象从容器中取出时,才会通知你出现了问题。在后一种情况中,你知道存在问题,但是不知道罪魁祸首在哪里,如果使用受检查的容器,就可以发现谁在试图插入不良对象。
让我们用受检查的容器来看看“将猫插入到狗的列表中”这个问题。这里,oldStyleMethod()表示遗留代码,因为它接受的是原生的List,而@SuppressWarnings(“unchecked”)注解对于压制所产生的警告是必须的:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import typeinfo.pets.Cat;
import typeinfo.pets.Dog;
import typeinfo.pets.Pet;
public class CheckedList {
@SuppressWarnings({ "unchecked", "rawtypes" })
static void oldStyleMethod(List probablyDogs) {
probablyDogs.add(new Cat());
}
public static void main(String[] args) {
List<Dog> dogs1 = new ArrayList<>();
oldStyleMethod(dogs1);
List<Dog> dogs2 = Collections.checkedList(new ArrayList<Dog>(), Dog.class);
try {
oldStyleMethod(dogs2);
} catch (Exception e) {
System.out.println(e);
}
List<Pet> pets = Collections.checkedList(new ArrayList<Pet>(), Pet.class);
pets.add(new Dog());
pets.add(new Cat());
}
}
运行这个程序时,你会发现插入一个Cat对于dogs1来说没有任何问题,而dogs2立即会在这个错误类型的插入操作上抛出一个异常。开可以看到,将导出类型的对象放置到将要检查基类型的受检查容器中是没有问题的。
二、异常
由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接或间接继承自Throwable(这将进一步阻止你去定义不能捕获的泛型异常)。
但是,类型参数可能会在一个方法的throws子句中用到。这使得你可以编写随检查型异常的类型而发生变化的泛型代码:
import java.util.ArrayList;
import java.util.List;
/**
* 处理器
*/
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<>();
for (Processor<T, E> processor : this) {
processor.process(resultCollector);
}
return resultCollector;
}
}
/**
* 故障1
*/
class Failure1 extends Exception {
}
/**
* 处理器1
*/
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();
}
}
}
/**
* 故障2
*/
class Failure2 extends Exception {
}
/**
* 处理器2
*/
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();
}
}
}
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) {
e.printStackTrace();
}
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) {
e.printStackTrace();
}
}
}
Processor执行process(),并且可能会抛出具有类型E的异常。process()的结果存储在List<T> resultCollector中(这被称为收集参数)。ProcessRunner有一个processAll()方法,它将执行所持有的每个Process对象,并返回resultCollector。
如果不能参数化所抛出的异常,那么由于检查型异常的缘故,将不能编写出这种泛化代码。
如果本文对您有很大的帮助,还请点赞关注一下。
还没有评论,来说两句吧...