EnumSet and EnumMap, special implementations of Set and Map for use with enums, and shown in the Enumerated Types chapter.
Collectipns 中的一些单元方法
你可以看到这个整个集合体系中有几个类是以 Abstract 开头的,这些用虚线框包裹的类表示抽象类。他们实现了对应接口的部分功能,比如当你想要实现一个 Set 接口的时候, 你不会想要实现 Set 接口并且实现里面的所有方法。一般来说,我们会通过继承 AbstractSet 类做一个最小实现。但是说实话现在集合类基本上能满足你的需求了,一般不需要自己做扩展。
publicclassFillingLists{ publicstaticvoidmain(String[] args){ List<StringAddress> list = new ArrayList<>( Collections.nCopies(4, new StringAddress("Hello"))); System.out.println(list); Collections.fill(list, new StringAddress("World!")); System.out.println(list); } }
publicclassCollectionData<T> extendsArrayList<T> { publicCollectionData(Generator<T> gen, int quantity){ for (int i = 0; i < quantity; i++) add(gen.next()); }
// A generic convenience method: publicstatic <T> CollectionData<T> list(Generator<T> gen, int quantity){ returnnew CollectionData<>(gen, quantity); } }
classGovernmentimplementsGenerator<String> { String[] foundation = ("strange women lying in ponds " + "distributing swords is no basis for a system of " + "government").split(" "); privateint index;
public String next(){ return foundation[index++]; } }
publicclassCollectionDataTest{ publicstaticvoidmain(String[] args){ Set<String> set = new LinkedHashSet<>( new CollectionData<>(new Government(), 15)); // Using the convenience method: set.addAll(CollectionData.list(new Government(), 15)); System.out.println(set); } }
// output: // [strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government]
classCircleextendsShape{ public String toString(){ return"Circle"; } }
classSquareextendsShape{ public String toString(){ return"Square"; } }
classTriangleextendsShape{ public String toString(){ return"Triangle"; } }
publicclassShapes{ publicstaticvoidmain(String[] args){ List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle()); for (Shape shape : shapeList) shape.draw(); } } // output: // Circle.draw() // Square.draw() // Triangle.draw()
在上面的例子中,我本将子类结合 List 强转成父类,然后统一做操作,这种做法更易读,容易维护。这也是面向对象的目标之一,但是如果我想在运行时得知这个对象的具体类型,应该怎么做?
The Class object
在 Java 中有一个神奇的类他叫 Class 类,所有创建类实例的行为都和他有关。 Java 的 RTTI 特性也是通过它来实现的。当你编译一个类的时候,JVM 会通过 class 创建一个对应的 Class 类来存储对应的信息。
类加载器由一组 class loaders 组成,但是已有一个 primordial class loader,他是 JVM 的一部分,他会加载所有的 trusted classes,这写 trusted class 包括 Java API classes, 比如本地磁盘上的 classes。通常你不需要自己新加 class loader 但是如果有特殊需要,想加也是可以的。
Java 5 中还为 Class 添加了 cast(object) 方法用于将参数强转为 Class 对应的类实例。
1 2 3 4 5 6 7 8 9 10 11 12
classBuilding{}
classHouseextendsBuilding{}
publicclassClassCasts{ publicstaticvoidmain(String[] args){ Building b = new House(); Class<House> houseType = House.class; House h = houseType.cast(b); h = (House) b; // ... or just do this. } }
使用括号的那种强转方式要比调用方法的方便很多,需要注意的是,一开始 new 的时候对应的实现是 House 才能在后面进行 b 强转成 a 的,如果你一开始声明为 new Building() 而且两边的方法有出入,运行时会抛异常
abstractclassPetCreator{ private Random rand = new Random(47);
// 模版方法 模式 // The List of the different types of Pet to create: publicabstract List<Class<? extends Pet>> types();
public Pet randomPet(){ // Create one random Pet int n = rand.nextInt(types().size()); try { return types().get(n).newInstance(); } catch (InstantiationException e) { thrownew RuntimeException(e); } catch (IllegalAccessException e) { thrownew RuntimeException(e); } }
public Pet[] createArray(int size) { Pet[] result = new Pet[size]; for (int i = 0; i < size; i++) result[i] = randomPet(); return result; }
public ArrayList<Pet> arrayList(int size){ ArrayList<Pet> result = new ArrayList<Pet>(); Collections.addAll(result, createArray(size)); return result; } }
classForNameCreatorextendsPetCreator{ privatestatic List<Class<? extends Pet>> types = new ArrayList<Class<? extends Pet>>(); // Types that you want to be randomly created: privatestatic String[] typeNames = { "review.Mutt", "review.Pug", "review.EgyptianMau", "review.Manx", "review.Cymric", "review.Rat", "review.Mouse", "review.Hamster" };
public List<Class<? extends Pet>> types() { return types; }
publicstaticvoidmain(String[] args){ System.out.println(types); } } // output: // [class review.Mutt, class review.Pug, class review.EgyptianMau, class review.Manx, class review.Cymric, class review.Rat, class review.Mouse, class review.Hamster]
新建一个 Pets 工具类用来创建创建 pet, 书上管这种方式叫做 Facade 模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import java.util.ArrayList;
publicclassPets{ publicstaticfinal PetCreator creator = new LiteralPetCreator();
publicstatic Pet randomPet(){ return creator.randomPet(); }
This also provides indirection to randomPet( ), createArray( ) and arrayList( ). Because PetCount.countPets( ) takes a PetCreator argument, we can easily test the LiteralPetCreator (via the above Facade):
publicstatic Part createRandom(){ int n = rand.nextInt(partFactories.size()); return partFactories.get(n).create(); } }
classFilterextendsPart{ }
classFuelFilterextendsFilter{ // Create a Class Factory for each specific type: publicstaticclassFactoryimplementsreview.Factory<FuelFilter> { public FuelFilter create(){ returnnew FuelFilter(); } } }
publicclassBlankFinal{ privatefinalint i = 0; // Initialized final privatefinalint j; // Blank final privatefinal Poppet p; // Blank final reference
// Blank finals MUST be initialized in the constructor: publicBlankFinal(){ j = 1; // Initialize blank final p = new Poppet(1); // Initialize blank final reference }
publicBlankFinal(int x){ j = x; // Initialize blank final p = new Poppet(x); // Initialize blank final reference }
publicstaticvoidmain(String[] args){ new BlankFinal(); new BlankFinal(47); } }
publicclassFinalOverridingIllusion{ publicstaticvoidmain(String[] args){ OverridingPrivate2 op2 = new OverridingPrivate2(); op2.f(); op2.g(); // You can upcast: OverridingPrivate op = op2; // But you can’t call the methods: //! op.f(); //! op.g(); // Same here: WithFinals wf = op2; //! wf.f(); //! wf.g(); } } // output: // OverridingPrivate2.f() // OverridingPrivate2.g()
If a method is private, it isn’t part of the base-class interface. It is just some code that’s hidden away inside the class, and it just happens to have that name.
finalclassDinosaur{ int i = 7; int j = 1; SmallBrain x = new SmallBrain();
voidf(){ } }
//! class Further extends Dinosaur {} // error: Cannot extend final class ‘Dinosaur’ publicclassJurassic{ publicstaticvoidmain(String[] args){ Dinosaur n = new Dinosaur(); n.f(); n.i = 40; n.j++; } }
final class 的 field 可以不是 final 的,但是 final class 里面的 method 都隐示为 final method。因为 final class 就是为了防止被继承,都不被继承了,对应的方法都不能重写也是合理的。
和前面的章节一样,这里的默认 final 也是语义上的,并不会在字节码中体现出来。
final caution
例举了一些老的 Java lib 实现 Vector 和 Hashtable 说明,使用 final 修饰方法的时候要谨慎,你完全不知道其他人会怎样使用你的代码。
publicclassRandomList<T> { private ArrayList<T> storage = new ArrayList<T>(); private Random rand = new Random(47);
publicvoidadd(T item){ storage.add(item); }
public T select(){ return storage.get(rand.nextInt(storage.size())); }
publicstaticvoidmain(String[] args){ RandomList<String> rs = new RandomList<String>(); for (String s : ("The quick brown fox jumped over " + "the lazy brown dog").split(" ")) rs.add(s); for (int i = 0; i < 11; i++) System.out.print(rs.select() + " "); } }
// output // brown over fox quick quick dog brown The brown lazy brown
数据结构很简单,随机性由 Random 对象提供 random.nextInt(x) 可以给出 0-x 返回内的整数。RandomList 里面新建一个 ArrayList 作为数据存储容器。
publicclassCoffeeGenerator implementsGenerator<Coffee>, Iterable<Coffee> { private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class,}; privatestatic Random rand = new Random(47);
publicCoffeeGenerator(){ }
// For iteration: privateint size = 0;
publicCoffeeGenerator(int sz){ size = sz; }
public Coffee next(){ try { return (Coffee)types[rand.nextInt(types.length)].newInstance(); // Report programmer errors at run time: } catch (Exception e) { thrownew RuntimeException(e); } }
classCoffeeIteratorimplementsIterator<Coffee> { int count = size;
publicbooleanhasNext(){ return count > 0; }
public Coffee next(){ count--; return CoffeeGenerator.this.next(); }
publicvoidremove(){ // Not implemented thrownew UnsupportedOperationException(); } }
public Iterator<Coffee> iterator(){ returnnew CoffeeIterator(); }
publicstaticvoidmain(String[] args){ CoffeeGenerator gen = new CoffeeGenerator(); for (int i = 0; i < 5; i++) System.out.println(gen.next()); for (Coffee c : new CoffeeGenerator(5)) System.out.println(c); } } // output // Americano 0 // Latte 1 // Americano 2 // Mocha 3 // Mocha 4 // Breve 5 // Americano 6 // Latte 7 // Cappuccino 8 // Cappuccino 9
下面是使用泛型接口实现斐波那契额的例子
算法这一块,不是我吹逼,我真的太弱了 (; ̄ェ ̄) 老是忘记
这里 class 内部持有一个 count 变量,每次调用 next() 方法,都会使得 count+1, 第 n 次调用就相当于打印 fib(n) 的值,fib 是一个基本的递归函数。
publicclassParcel2{ classContents{ privateint i = 11;
publicintvalue(){ return i; } }
classDestination{ private String label;
Destination(String whereTo) { label = whereTo; }
String readLabel(){ return label; } }
public Destination to(String s){ returnnew Destination(s); }
public Contents contents(){ returnnew Contents(); }
publicvoidship(String dest){ Contents c = contents(); Destination d = to(dest); System.out.println(d.readLabel()); }
publicstaticvoidmain(String[] args){ Parcel2 p = new Parcel2(); p.ship("Tasmania"); Parcel2 q = new Parcel2(); // Defining references to inner classes: Parcel2.Contents c = q.contents(); Parcel2.Destination d = q.to("Borneo"); } }
// output: Tasmania
官方给的例子有点不太好记忆, 本人更倾向于简单的直接 Outer/Inner 这中名字来命名 class
1 2 3 4 5 6 7 8 9 10 11
publicclassOuter{ classInner{}
public Inner getInner(){ returnnew Inner(); }
publicstaticvoidmain(String[] args){ Outer.Inner inner = new Outer().getInner(); } }
The link to the outer class
内部类最显著的特点: Inner class 创建的时候会持有一个外部类的引用, 概念上类似指针, 这使得他能没有限制的访问外部类成员变量和方法.
public Destination destination(String s){ returnnew PDestination(s); }
public Contents contents(){ returnnew PContents(); } }
Exc8: 确认下外部类是否能访问内部类变量? Determine whether an outer class has access to the private elements of its inner class. 看调用方式, 如果是外部类方法直接调用内部类成员变量, 不能, 外部类实例化后, 内部类可能压根就没有实例化, 访问个毛线 如果是实例化了, 就可以调用, 习题答案如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classOuter8{ classInner{ privateint ii1 = 1; privateint ii2 = 2; privatevoidshowIi2(){ System.out.println(ii2); } privatevoidhi(){ System.out.println("Inner hi"); } } // Need to create objects to access private elements of Inner: int oi = new Inner().ii1; voidshowOi(){ System.out.println(oi); } voidshowIi2(){ new Inner().showIi2(); } voidouterHi(){ new Inner().hi(); } publicstaticvoidmain(String[] args){ Outer8 out = new Outer8(); out.showOi(); out.showIi2(); out.outerHi(); } }
Inner classes in methods and scopes
前面那些例子都很直白易懂, 但是 Inner class 还有一些变种, 格式很放飞自我, 该变种适用如下情况
你只是想要实现某个接口, 并返回这个接口引用
你在解决某个复杂问题时, 临时需要创建一个 class 以解决问题, 但是不想暴露它的实现
下面我们会将前面的 Parcel 例子转化为以下几种方式:
1.A class defined within a method - 在方法体内定义类 2.A class defined within a scope inside a method - 在方法的某个更小的 scope 中声明类, 比如方法的 if 条件语句中 3.An anonymous class implementing an interface - 匿名内部类实现接口 4.An anonymous class extending a class that has a non-default constructor - 匿名内部类继承抽象类 + 自定义构造函数 5.An anonymous class that performs field initialization - 匿名内部类 + field 初始化 6.An anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors) - 匿名内部类 + 构造代码块
对应 item1: A class defined within a method, 我们将 class 创建在方法体内部, 这种做法也叫 本地内部类(local inner class):
publicclassParcel6{ privatevoidinternalTracking(boolean b){ if (b) { classTrackingSlip{ private String id;
TrackingSlip(String s) { id = s; }
String getSlip(){ return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } // Can’t use it here! Out of scope: // ! TrackingSlip ts = new TrackingSlip("x"); }
publicvoidtrack(){ internalTracking(true); }
publicstaticvoidmain(String[] args){ Parcel6 p = new Parcel6(); p.track(); } }
TrackingSlip 嵌在 if 语句中, 只在 if 里生效, 出了这个范围就失效了, 除此之外和其他内部类没什么区别.
Anonymous inner classes
对应 item3: An anonymous class implementing an interface 定义匿名内部类 和 item4: An anonymous class extending a class that has a non-default constructor 使用默认构造函数
publicclassParcel7b{ classMyContentsimplementsContents{ privateint i = 11;
publicintvalue(){ return i; } }
public Contents contents(){ returnnew MyContents(); }
publicstaticvoidmain(String[] args){ Parcel7b p = new Parcel7b(); Contents c = p.contents(); } }
看文章顺序这个应该是对应 item4: An anonymous class extending a class that has a non-default constructor 的但是总感觉他这种说法不贴切, 可能是我笔记有问题, 按理说, 下面的 instance initialization 更贴切才对.
publicstaticvoidmain(String[] args){ Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); System.out.println(wrapping.value()); } }
publicclassWrapping{ privateint i;
publicWrapping(int x){ i = x; }
publicintvalue(){ return i; } }
// output: 470
对应 item5: An anonymous class that performs field initialization 你可以在内部类中定义, 使用 field, field 如果是作为参数传入, 必须是 final 类型的:
再看一遍才发现, 他的特殊之处是内部类有一个 field 声明, 对应的值是直接从方法参数里面拿的!!这种用法以前没注意到过 (; ̄ェ ̄)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassParcel9{ // Argument must be final to use inside // anonymous inner class: public Destination destination(final String dest){ returnnew Destination() { private String label = dest;
public String readLabel(){ return label; } }; }
publicstaticvoidmain(String[] args){ Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); } }
If you’re defining an anonymous inner class and want to use an object that’s defined outside the anonymous inner class, the compiler requires that the argument reference be final, as you see in the argument to destination(). If you forget, you’ll get a compile-time error message.
Remember the advice given at the end of the last chapter: Prefer classes to interfaces. If your design demands an interface, you’ll know it. Otherwise, don’t put it in until you are forced to.
一般来说, 在 interface 里放 class 是不允许的, 但是 nested class 是个例外. 任何放到 interface 里的 code 都会有 public 和 static 的属性, 所以下面代码中声明的 class class Test implements ClassInInterface 其实就是一个静态内部类. You can even implement the surrounding interface in the inner class, like this:
In a single outer class you can have several inner classes, each of which implements the same interface or inherits from the same class in a different way. An example of this will be shown shortly.
The point of creation of the inner-class object is not tied to the creation of the outer-class object.
There is no potentially confusing “is-a” relationship with the inner class; it’s a separate entity.
publicclassController{ // A class from java.util to hold Event objects: private List<Event> eventList = new ArrayList<Event>();
publicvoidaddEvent(Event c){ eventList.add(c); }
publicvoidrun(){ while (eventList.size() > 0) // Make a copy so you’re not modifying the list // while you’re selecting the elements in it: for (Event e : new ArrayList<Event>(eventList)) if (e.ready()) { System.out.println(e); e.action(); eventList.remove(e); } } }
publicvoidaction(){ // Put hardware control code here. thermostat = "Day"; }
public String toString(){ return"Thermostat on day setting"; } }
// An example of an action() that inserts a // new one of itself into the event list: publicclassBellextendsEvent{ publicBell(long delayTime){ super(delayTime); }
publicclassGreenhouseController{ publicstaticvoidmain(String[] args){ GreenhouseControls gc = new GreenhouseControls(); // Instead of hard-wiring, you could parse // configuration information from a text file here: gc.addEvent(gc.new Bell(900)); Event[] eventList = { gc.new ThermostatNight(0), gc.new LightOn(200), gc.new LightOff(400), gc.new WaterOn(600), gc.new WaterOff(800), gc.new ThermostatDay(1400) }; gc.addEvent(gc.new Restart(2000, eventList)); gc.addEvent(new GreenhouseControls.Terminate(new Integer(5000))); gc.run(); } } // output: // Bing! // Thermostat on night setting // Light is on // Light is off // Greenhouse water is on // Greenhouse water is off // Thermostat on day setting // Restarting system // Terminating
This example shows that there isn’t any extra inner-class magic going on when you inherit from the outer class. The two inner classes are completely separate entities, each in its own namespace. However, it’s still possible to explicitly inherit from the inner class:
Counter getCounter(final String name){ // A local inner class: classLocalCounterimplementsCounter{ publicLocalCounter(){ // Local inner class can have a constructor System.out.println("LocalCounter()"); }
publicintnext(){ System.out.print(name); // Access local final return count++; } } returnnew LocalCounter(); }
// The same thing with an anonymous inner class: Counter getCounter2(final String name){ returnnew Counter() { // Anonymous inner class cannot have a named // constructor, only an instance initializer: { System.out.println("Counter()"); }
publicintnext(){ System.out.print(name); // Access local final return count++; } }; }
publicstaticvoidmain(String[] args){ LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner "); for (int i = 0; i < 5; i++) System.out.println(c1.next()); for (int i = 0; i < 5; i++) System.out.println(c2.next()); } }
// output // LocalCounter() // Counter() // Local inner 0 // Local inner 1 // Local inner 2 // Local inner 3 // Local inner 4 // Anonymous inner 5 // Anonymous inner 6 // Anonymous inner 7 // Anonymous inner 8 // Anonymous inner 9
上面的例子中, Counter 接口会依次返回 count 值. local inner class 和 匿名内部类都实现了这个接口. 两个内部类逻辑和功能也都一样, 唯一区别是, 匿名内部类他是没有构造函数的, 需要用代码块代替.
如果你需要创建多个实例的话, 你也要使用 local inner class, 你用 anonymous 是建不出来多个实例的.