Thinking in Java
元组
1、元组:将一组对象直接打包储存于其中的一个单一对象,该容器对象允许读取其中的元素,但是不允许向其中存放新对象;(即
数据传输对象,信使);
2、在Java中可以通过
泛型实现元组对象;
3、Java泛型有一个局限性:
基本类型无法作为类型参数,但是Java SE5具备自动打包和自动拆包功能;
泛型方法
1、使用泛型的基本原则:在任何情况下,
尽量使用泛型方法,如果可以使用泛型方法取代整个泛型类,那就应该使用泛型方法;
2、可变参数和泛型方法
可变参数和泛型方法可以很好地共存:
public static <T> List<T> makeList(T...args){
List<T> result = new ArrayList<T>();
for(T item : args)
result.add(item);
return result;
}
匿名内部类
泛型可以应用匿名内部类:
interface Generator<T>{
public T next();
}
class Customer{
......//内部类
public static Generator<Customer> getGenerator(){
return new Generator<Customer>(){
public Customer next(){ return new Customer();}
}
}
}
class Teller{
......//内部类
public static Generator<Teller> getGenerator(){
return new Generator<Teller>(){
public Teller next(){ return new Teller();}
}
}
}
类型擦除
1、
在泛型代码内部,无法获取任何有关泛型参数类型的信息;
2、
Java泛型时使用擦除实现的,当使用泛型代码时,
任何具体的类型信息都会被擦除;而你唯一知道的是你在使用一个对象,List<String>和List<Integer>在运行的事实上都是相同的类型(这两种类型都会被擦除为它们的原生类型,即 List 或 List<Object>);
3、Java对类型擦除的补偿
(1)泛型对象T无法创建类型实例
在Java中,创建一个new T()是无法实现的,解决方案是传递一个工厂对象,并使用它来创建新的实例;其中最便利的工厂对象时Class类,使用()方法来创建该类型的新对象;
Class<T> type = T.class;
T demo = type.newInstance();
※这种方法有缺陷:T类型可能没有默认的构造方法,如Integer,此时建议使用显性工厂对其进行限制;
interface Factory<T>{ //工厂类
T create();
}
class Product<T>{ //产品类,转发泛型类型的载体,实现泛型T的实例化;
private T x;
public <F extends Factory<T>> Product(F factory){
x = factory.create();
}
}
//具体工厂类,回避Integer没有默认的构造方法,而带来的编译期无法捕获的异常;
class IntegerFactory implements Factory<Integer>{
public Integer create(){ return new Integer(0); }
}
//对某个类型使用其内部工厂类
class Widget{
public static class WidgetFactory implements Factory<Widget>{
public Widget create(){ return new Widget(); }
}
}
main(){
new Productor<Inetegr>(new IntegerFactory());
new Prodoctor<Widget>(new Widget.WidgetFactory());
}
(2)无法创建泛型数组
①在任何想要创建泛型数组的地方使用
ArrayList<T>;
List<T> list = new ArrayList<T>();
②在集合内部使用
Object[],然后使用数组元素时,添加一个
T的转型;(一般是在访问数据时进行转型)
class GenericArray{
private Object[] array;
public GenericArray(int size){
array = new Object[size];
}
public void put(int index,T item){
array[index] = item;
}
@SuppressWarnings("unchecked") //抑制编译时类型擦除引起的unchecked检查警告;
public T get(int index){
return (T)array[index];
}
@SuppressWarnings("unchecked")
public T[] rep(){
return (T[])array;
}
}
15.5 通配符
1、List<Number> 不是List<Integer>的父类
,List<? extends Number>才是
List<Integer>的父类;
2、无界通配符<?>
(1)
List 等同于 List<Object>:持有任何Object类型的原生List;
(2)
List<?>:具有某种特定类型的非原生List(只是暂时不清楚该类型具体时什么);)
3、List(同List<Object>) 、List<?>、List<? extends Object>声明的对象可以自由相互传递参数,但是:
void method(List<?> list1){
List<? extends Object> list = list1; //会抛出unchecked警告,必要时使用@SuppressWarning("unchecked")阻止;
}
4、转型捕获:
在一种情况下需要使用<?>,如果向一个使用<?>的方法传递一原生类型,编译器可能会推断出实际的参数类型,使该方法可以回转并调用另一个使用这个确切类型的方法;
//
未指定的通配符类型被捕获,并转化为确切的类型;
class Holder<T>{
private T value;
public Holder(){ }
public Holder(T value){ this.value = value;}
public void set(T value){ this.value = value;}
public T get(){ return value;}
}
public CaptureConversion{
//接收无界参数为类型,转化为确切类型参数
static <T> void incept(Holder<T> holder){
T t = holder.get();
System.out.println(t.getClass().getSimpleName()); //打印接收类泛型参数类型
}
//转发无界类型参数
static void transmit(Holder<?> hodler){
incept(holder);
}
//测试,移除警告阻止后,以下警告才会报出;
@SuppressWarning("uncheked");
public static void main(){
Holder h1 = new Holder<Integer>(1);
transmit(h1); //no warning,h1成功被转化为Holder<Integer>类型;
//incpet(h1); warning;
Holder h2 = new Holder();
transmit(h2); //no warning,h2被转化为Holder<Object>类型;
Holder<?> h3 = new Holder<Double>(1.0);
transmit(h3);
}
}
15.6 自限定的类型
class SelfBounded<T extends SelfBounded<T>>{ ..... }
古怪的循环(CRG):类古怪地出现在自己的基类中;
1、CRG:基类用导出类代替其参数,者意味着
泛型基类变成一种导出类的公共功能的模板,但是这些导
出类对于所有的参数和返回值,将使用导出类型;
public class BasicHolder<T>{
T element;
void set(T args){ element = args; }
T get(){ return elements; }
void f (){ System.out.printn(element.getClass().getSimpleName()); }
}
class Subtype extends BasicHolder<Subtype>{ }
main(){
Subtype st1 = new Subtype();
Subtype st2 = new Subtype();
st1.set(st2);
Subtype st3 = st1.get();
}
//相当于Subtype把BasicHolder当成一种公共模版、类似于C++中的模版类;
2、自限定通过额外的步骤,强制泛型当做自己的边界参数使用;
3、自限定类型的价值在于:
它们可以产生协变参数类型——方法参数类型会随子类而变化;
15.7 动态类型安全
1、因为可以向Java SE5之前的代码传递泛型容器,所以旧代码可能会破坏该容器,在Java SE5的 java
.util.Collections 中有一组便利工具,可检查这种情况下的类型检查问题;
2、这些便利工具为:
静态方法checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()、checkedSortedSet();
Collection<E> checkedCollection(Collection<E> c, Class<T> type);
//返回一个 c 对象 的动态安全视图,该动态视图只允许type类型的数据可以加入到容器中,否则会抛出ClassCastException异常;
如:
List<Integer> srcList = new List<Integer>();
List<Integer> list = Colleactions.checkedList(srcList, Integer.class);
list.add(new Double(2.0)); //throws ClassCastException;
3、动态安全视图主要与调试,在Java容器类的泛型实现中,泛型类型是以Object存储在容器中,只有在取出时才会被转型为T,可能在此时才会报错,
使用动态视图可以在加入步骤报告错误类型;
15.8 泛型异常
1、由于擦除的原因,泛型应用于异常很受限,
catch语句不能捕获泛型类型的异常,因为在编译期和运行期都必须知道异常的确切类型;
2、异常类不能直接或间接继承自Throwable;
3、实现泛型异常的方式:
//①定义抛出泛型异常的过程接口,定义过程抛出泛型异常;
interface Processer<T , E extends Exception>{
void process(T ) throws E;
}
//在某个具体参数过程中,根据需求抛出具体类型异常;
class Faiure1 extends Exception() { }
class Processer1 implements Processer<String ,Failure1>{
public void process(String str) throws Failure1{
if(str.length()<20)
throw new Failure("The length of the String is too short");
}
}
class Failure2 extends Exception() { }
public Processer2 implements<Integer,Failure2>{
public void process(Integer num) thorws Failure2{ if() thorw new Failure2();}
}
//测试
main(){
Process1 p1 = new Process1();
Process2 p2 = new Process2();
try{
p1.process("hello");
p2.process(233);
}
catch(Failure1 f1){ f1.printStackTrace(); }
catch(Failure2 f2){ f2.printStackTrace(); }
}
15.9 Java泛型使用中的一些问题
1、任何基本类型都不能作为类型参数;
2、实现参数化接口:一个类不能同时实现同一种泛型接口的2种变体,由于擦除的原因,这两个变体会成为一样的接口;
interface Pet<T>{ }
class animal implements Pet<Cat>,Pet<Dog>{ } //error:
3、重载:
由于擦除的原因,重载方法会产生相同的类型签名,无法通过编译;
public class Demo<W,T>{
void f(List<T> v){ }
void f(List<W> v){ }
}//无法通过编译
15.10 混型
1、混型:混合多个类的能力,以产生一个可以表示混型中所有类型的类,它使组装多个类变得简单易行;
2、混型的价值之一是它们可以将特性和行为一致地应用在多个类之上,如果想在混型类中修改某些东西,这些修改将会应用在混型所应用的所有类上;
3、常用的Java混型方式:
(1)与接口混型:某个类通过实现多个定义不同的接口实现混型;
(2)使用装饰器模式:装饰器模式使用对象来动态透明地向当个当个对象中添加责任;
class Basic{
private String value;
public void set(String value){ this.value = value; }
public String get(){ return value; }
}
class Decorator extends Basic{
private Basic basic;
public Decorator(Basic basic){ this.basic = basic; }
public void set(String val){ basic.set(val); }
public String get(){ basic.get();}
}
class TimeStamped extends Decorator{
private final long timeStamp;
public TimeStamped(Basic baisc ){
super(basic);
timeStamp = new Date();
}
public long getStamp(){ return timeStamp; }
}
(3)与动态代理混合:可以使用动态代理创建一种比装饰器更加贴近装饰器模型的机制,通过动态代理,所产生类的动态类型将会是已经混入的组合类型;
15.10 潜在类型机制
1、潜在类型机制是一种代码组织和复用机制:编写一次,多次使用,并在一个位置保存代码;(由此可以不必明确地命名代码上的确切接口,以节省代码量);
2、两种支持潜在类型机制的语言为:Python和C++;
3、Java对潜在类型机制的补偿:
使用反射:
public class Mime {
public void sit(){ System.out.println("Pretending to sit");}
public String toString(){ return "mime";}
public void pushInvisibleWall(){ }
}
public class Robot {
public void speak(){ System.out.println("BalBalaBala"); }
public void work(){ }
public String toString(){ return "robot";}
}
public class CommunicateReflectively {
public static void perform(Object speaker){
Class<?> spkr = speaker.getClass();
try{
try{
Method speak = spkr.getMethod("speak");
speak.invoke(speaker);
}catch(NoSuchMethodException ex){
System.out.println(speaker+" cannot not speak!");
}
try{
Method sit = spkr.getMethod("sit");
sit.invoke(speaker);
}catch(NoSuchMethodException ex){
System.out.println(speaker+" cannot not sit!");
}
}catch(Exception ex){
throw new RuntimeException(speaker.toString(),ex);
}
}
public static void main(String[] args){
perform(new Robot());
perform(new Mime());
}
}