浅谈Java匿名类,兼论模板模式
1 abstract class Person { 2 public abstract void eat(); 3 } 4 5 public class Demo { 6 public static void main(String[] args) { 7 Person p = new Person() { 8 public void eat() { 9 System.out.println("eat something"); 10 } 11 }; 12 p.eat(); 13 } 14 }
以上是一个匿名类的例子。
匿名类的书写格式就是:(注意,这个类可不一定非得是抽象类,普通类也可以)
new 类名或者接口名() { 重写方法(); }
作用:我们可以直接new一个抽象类或者接口,省去了去定义一个Person的子类的麻烦。这种用法多在于子类只使用一次的情况下。
但它的主要作用不是为了程序员写起来省点事,而是为了在匿名类里直接的访问匿名类外的成员和方法。
1 abstract class Person { 2 public abstract void eat(); 3 } 4 5 public class Demo { 6 public static void main(String[] args) { 7 String me = "I am coming"; 8 Person p = new Person() { 9 public void eat() { 10 System.out.println("eat something"); 11 System.out.println(me); 12 } 13 }; 14 p.eat(); 15 } 16 }
如你所见,匿名类的对象可以直接使用该对象所在的作用域里允许使用的所有变量。在这个静态main方法里,允许它直接使用字符串对象me. 如果不用匿名类,用子类来写,该怎么写才能在eat()方法里打印me呢?
package javaFx; abstract class Person { public abstract void eat(); } class Child extends Person { private String me; public void eat() { System.out.println("eat something"); System.out.println(me); } public void setMe(String me){ this.me = me; } } public class Demo { public static void main(String[] args) { String me = "I'm coming"; Child child = new Child(); child.setMe(me); Person p = (Person)child; p.setMe(me); p.eat(); } }
如上所见,我们不得不想办法将me这个对象传递到Person的对象里面,然后Person才能打印me.而匿名类不需要,匿名类直接拾取调用者身上的变量,直接使用。
匿名类的这个特性有点像特洛伊木马。也不奇怪,匿名类本就是个内部类,它既然是内部类,当然可以访问外部类的成员和方法。
但匿名类模糊了类之间的界限,违背了面向对象的精神。因为一般情况下,类只能处理类内部的数据,这才叫内聚程度高!对于类外部的数据,它们必须被传递到类的内部后,才能被类处理。这种传递就是所谓的类之间的消息传递。而匿名类可以直接使用外部类的成员和属性,根本不需要什么消息传递,这个匿名类就是和外部类高度耦合在了一起。所以,匿名类也不能被其它类复用,只能在一处被使用一次,它本质上只是外部类的一个部分。
匿名类一般用于回调,就像这样
1 String s1 = "I'm ok"; 2 doSomething( new Person(){ 3 public void aaa( String s ) throws SQLException{ 4 System.out.println(s); 5 System.out.println(s1) 6 } 7 } ); 8 9 private void doSomething(Person p){ 10 //doSomething 11 ... 12 s = "weird"; 13 p.aaa(s); 14 //continue doing something 15 ... 16 }
回调主要用于调用者不关心被调用者的情况。这里调用者是doSomething()这个方法,也可以说调用者是doSeomthing()所属于的类,被调用者是 new Person()对象。
在doSomething这个方法里,它不关心被调用者p是Person的哪个子类,它只要调用p.aaa(s);
被调用者是new Person()
doSomething()方法相当于一个模板,它有很多行代码,相当于做了很多事,其中的一件事是调用Person对象的aaa方法。这里,Person对象是动态绑定的,p.aaa(s)的行为是变化的,因为不同的子类实现,行为不同,而其它代码行是不变的,这样,不变的部分就是可以被重用的模板。这就是一个模板模式的例子。模板模式,就是用回调实现的。
当你写代码时发现有很多代码相同,但又有一些行不同,像下面这样,你可以考虑模板模式。
场景 1 print("我是张三"); print("我很喜欢画画"); print("我很喜欢钓鱼"); print("我很喜欢打麻将"); 场景 2 print("我是李四"); print("我很喜欢画画"); print("我很喜欢钓鱼"); print("我很喜欢打麻将"); 场景 3 print("我是王五"); print("我很喜欢画画"); print("我很喜欢钓鱼"); print("我很喜欢打麻将");
为了消灭重复代码,我们定义一个模板
模板 public void description(Me me){ me.printMe(); print("我很喜欢画画"); print("我很喜欢钓鱼"); print("我很喜欢打麻将"); } 将变化的部分定义成接口 Interface Me{ printMe(); }
场景 1 的代码就可以这样写
场景 1 description(new Me(){ public void printMe(){ print("我是张三"); })
你可以看到,在场景 1里,你再也不用写下面这样的重复代码了。同样的,在场景2和3里,也不用写这些重复代码了,因为这些重复代码都被集中到模板里去了。
print("我很喜欢画画"); print("我很喜欢钓鱼"); print("我很喜欢打麻将");