抽象类和接口
1. 抽象类
抽象类中不能有private abstract,为什么?子类没有方法对它进行合理的定义
// interfaces/AbstractAccess.java abstract class AbstractAccess { private void m1() {} // private abstract void m1a(); // illegal protected void m2() {} protected abstract void m2a(); void m3() {} abstract void m3a(); public void m4() {} public abstract void m4a(); }
创建抽象类和抽象方法是有帮助的,因为它们使得类的抽象性很明确,并能告知用户和编译器使用意图。抽象类同时也是一种有用的重构工具,使用它们使得我们很容易地将沿着继承层级结构上移公共方法。
2. 接口
Java 8 中接口稍微有些变化,因为 Java 8 允许接口包含默认方法和静态方法——基于某些重要原因,看到后面你会理解。接口的基本概念仍然没变,介于类型之上、实现之下。接口与抽象类最明显的区别可能就是使用上的惯用方式。接口的典型使用是代表一个类的类型或一个形容词,如 Runnable 或 Serializable,而抽象类通常是类层次结构的一部分或一件事物的类型,如 String 或 ActionHero。
接口同样可以包含属性,这些属性被隐式指明为 static 和 final。
// interfaces/ImplementingAnInterface.java interface Concept { // Package access void idea1(); void idea2(); } class Implementation implements Concept { @Override public void idea1() { System.out.println("idea1"); } @Override public void idea2() { System.out.println("idea2"); } }
3. 默认方法default
之前我对它只在switch中有接触,其实在jdk-8后,已经可以在接口中加方法的实现了。比如这个default
// interfaces/InterfaceWithDefault.java interface InterfaceWithDefault { void firstMethod(); void secondMethod(); default void newMethod() { System.out.println("newMethod"); } }
// interfaces/Implementation2.java public class Implementation2 implements InterfaceWithDefault { @Override public void firstMethod() { System.out.println("firstMethod"); } @Override public void secondMethod() { System.out.println("secondMethod") } public static void main(String[] args) { InterfaceWithDefault i = new Implementation2(); i.firstMethod(); i.secondMethod(); i.newMethod(); } }
firstMethod
secondMethod
newMethod
4. 多继承,这里指的是多实现实现多继承,举几个特殊的例子
package Test; // interface/MICollision.java import java.util.*; interface Bob1 { default void bob() { System.out.println("Bob1::bob"); } } interface Bob2 { default void bob() { System.out.println("Bob2::bob"); } } class Bobs implements Bob1, Bob2{ @Override public void bob() { Bob2.super.bob(); } } // class Bob implements Bob1, Bob2 {} /* Produces: error: class Bob inherits unrelated defaults for bob() from types Bob1 and Bob2 class Bob implements Bob1, Bob2 {} ^ 1 error */ interface Sam1 { default void sam() { System.out.println("Sam1::sam"); } } interface Sam2 { default void sam(int i) { System.out.println(i * 2); } } // This works because the argument lists are distinct: class Sam implements Sam1, Sam2 {} interface Max1 { default void max() { System.out.println("Max1::max"); } } interface Max2 { default int max() { return 47; } }
// class Max implements Max1, Max2 {} /* Produces: error: types Max2 and Max1 are imcompatible;
both define max(), but with unrelated return types class Max implements Max1, Max2 {} ^ 1 error */
其中第一个的解决方法已经列出,这种调用我也是第一次见,为什么可以直接掉,原因估计还是类已经加载完毕在实际运行到的时候
5. 接口中的静态方法,要静态,不能实例化的东西必须要让它在加载时搞好。
// onjava/Operations.java package onjava; import java.util.*; public interface Operations { void execute(); static void runOps(Operations... ops) { for (Operations op: ops) { op.execute(); } } static void show(String msg) { System.out.println(msg); } }
这里的runOps就像一个模板方法一样,其实现还要看下面。
// interface/Machine.java import java.util.*; import onjava.Operations; class Bing implements Operations { @Override public void execute() { Operations.show("Bing"); } } class Crack implements Operations { @Override public void execute() { Operations.show("Crack"); } } class Twist implements Operations { @Override public void execute() { Operations.show("Twist"); } } public class Machine { public static void main(String[] args) { Operations.runOps( new Bing(), new Crack(), new Twist()); } }
这里因为在main中进行set值得时候就已经确保了这个类都是接口得实现方式,所以不用担心类是否实现。后面为什么会去走实现类中得方法,这就是一个典型得多态运用。
Bing
Crack
Twist
对于抽象类中的构造器,实际是不是实例化抽象类,而是当子类的方法全覆盖抽象类后用抽象类的构造来实例化子类。
6. 完全解耦
// interfaces/interfaceprocessor/Processor.java package interfaces.interfaceprocessor; public interface Processor { default String name() { return getClass().getSimpleName(); } Object process(Object input); } // interfaces/interfaceprocessor/Applicator.java package interfaces.interfaceprocessor; public class Applicator { public static void apply(Processor p, Object s) { System.out.println("Using Processor " + p.name()); System.out.println(p.process(s)); } }
// interfaces/interfaceprocessor/StringProcessor.java // {java interfaces.interfaceprocessor.StringProcessor} package interfaces.interfaceprocessor; import java.util.*; interface StringProcessor extends Processor { @Override String process(Object input); // [1] String S = "If she weighs the same as a duck, she's made of wood"; // [2] static void main(String[] args) { // [3] Applicator.apply(new Upcase(), S); Applicator.apply(new Downcase(), S); Applicator.apply(new Splitter(), S); } } class Upcase implements StringProcessor { // 返回协变类型 @Override public String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase implements StringProcessor { @Override public String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter implements StringProcessor { @Override public String process(Object input) { return Arrays.toString(((String) input).split(" ")); } }
Using Processor Upcase IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD Using Processor Downcase if she weighs the same as a duck, she's made of wood Using Processor Splitter [If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]
如果用继承也可以完成这项工作,但是继承就意味着和上层类绑定了。
7. 接口也可以继承接口,这点还经常在重构用
// interfaces/HorrorShow.java // Extending an interface with inheritance interface Monster { void menace(); } interface DangerousMonster extends Monster { void destroy(); } interface Lethal { void kill(); } class DragonZilla implements DangerousMonster { @Override public void menace() {} @Override public void destroy() {} } interface Vampire extends DangerousMonster, Lethal { void drinkBlood(); } class VeryBadVampire implements Vampire { @Override public void menace() {} @Override public void destroy() {} @Override public void kill() {} @Override public void drinkBlood() {} } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v(DangerousMonster d) { d.menace(); d.destroy(); } static void w(Lethal l) { l.kill(); } public static void main(String[] args) { DangerousMonster barney = new DragonZilla(); u(barney); v(barney); Vampire vlad = new VeryBadVampire(); u(vlad); v(vlad); w(vlad); } }
当打算组合接口时,在不同的接口中使用相同的方法名通常会造成代码可读性的混乱,尽量避免这种情况。
8. 常量接口,禁止这么用,要用enum
9. 接口嵌套,这个可以用,但是很少这么用吧。而且对于private和public的控制也较麻烦,我感觉这符合面向对象,但是是不实用的。
10. 接口和工厂模式
// interfaces/Factories.java interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Service1 implements Service { Service1() {} // Package access @Override public void method1() { System.out.println("Service1 method1"); } @Override public void method2() { System.out.println("Service1 method2"); } } class Service1Factory implements ServiceFactory { @Override public Service getService() { return new Service1(); } } class Service2 implements Service { Service2() {} // Package access @Override public void method1() { System.out.println("Service2 method1"); } @Override public void method2() { System.out.println("Service2 method2"); } } class Service2Factory implements ServiceFactory { @Override public Service getService() { return new Service2(); } } public class Factories { public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(new Service1Factory()); // Services are completely interchangeable: serviceConsumer(new Service2Factory()); } }
工厂模式就是不直接通过new 来调用构造函数,其实本质上也是调用了,否则怎么创建呢。所谓设计模式就是将白话包装成文学作品,这个包装需要很厉害的功底和大量的编程经验,我现在对设计模式也是仅仅停留在会写不会用的程度。
认为接口是好的选择,从而使用接口不用具体类,这具有诱惑性。几乎任何时候,创建类都可以替代为创建一个接口和工厂。
很多人都掉进了这个陷阱,只要有可能就创建接口和工厂。这种逻辑看起来像是可能会使用不同的实现,所以总是添加这种抽象性。这变成了一种过早的设计优化。
任何抽象性都应该是由真正的需求驱动的。当有必要时才应该使用接口进行重构,而不是到处添加额外的间接层,从而带来额外的复杂性。这种复杂性非常显著,如果你让某人去处理这种复杂性,只是因为你意识到“以防万一”而添加新接口,而没有其他具有说服力的原因——好吧,如果我碰上了这种设计,就会质疑此人所作的所有其他设计了。
恰当的原则是优先使用类而不是接口。从类开始,如果使用接口的必要性变得很明确,那么就重构。接口是一个伟大的工具,但它们容易被滥用。