9.1抽象类和抽象接口
创建抽象类是希望通过这个通用类操纵一系列类,所以如果直接创建一个抽象类是没有意义的.甚至还可以在所有的方法都产生错误.含有抽象方法的类必须定义为抽象类.
抽象类是很重要的重构工具,他可以使我们很容易的将共用方法沿继承结构向上移动.
9.2接口
接口是指”所有实现了该接口的类都有这样的行为”.接口用于建立类与类之间的协议.
接口定义
public interface{}
如果是
interface{}
没有任何修饰,表示包内可以访问.接口内可以包含域(成员变量和常量)但是,这些域隐式的申明为 static final.
接口内的所有域和方法 默认是public权限,即 不需要显示的设置,并且 不能设置除了public 之外其他的任何权限(private,protected);
ackage tinking_in_java.interfacetest;
import java.util.Random;
/**
* Created by leon on 17-12-15.
*/
public interface Interface {
int i = new Random().nextInt();
int j = new Value().getValue();
void test();
}
package tinking_in_java.interfacetest;
/**
* Created by leon on 17-12-15.
*/
public class Value {
public int getValue() {
return 10;
}
}
package tinking_in_java.interfacetest;
/**
* Created by leon on 17-12-15.
*/
public class TestImp implements Interface {
@Override
public void test() {
// i = 20; 不能修改,因为已经是final 类型
System.out.println(i);
}
public static void main(String[] args) {
TestImp testImp = new TestImp();
testImp.test();
}
}
//output----
10
在java 8中接口引入了关键字default 方法,可以在接口中实现,但是对于一个类实现了两个不同的接口,但是这两个接口都有相同的default 方法,实现类必须overriding default方法否则编译会报无法关联default.
public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}
public interface B {
default void foo(){
System.out.println("Calling B.foo()");
}
}
public class Clazz implements A, B {}
上面代码编译失败,报错如下:java: class Clazz inherits unrelated defaults for foo() from types A and B
为了修复错误,在Clazz中,我们手工地覆写掉冲突的方法来处理这个问题,如下所示:
public class Clazz implements A, B {
public void foo(){}
}
可是,如果我们就想调用接口A中默认实现的方法foo(),而不用自己实现的,该怎么办?那就要像下面这样使用A#foo()才行。
public class Clazz implements A, B {
public void foo(){
A.super.foo();
}
}
9.3完全解耦
复用代码的第一种方式是:客户端程序遵循接口来编写自己的类.但是有时你发现接口所需要的传递的数据类型,并不是你想传递的,或者说已经定义好的接口所接受的类型是过时的,为了保持还是使用之前的接口方法,需要对数据进行重新适配(建立中间类,实现旧的接口,在里面重新替换新的实现()).这种模式也叫适配器模式.具体
UML结构图:

public interface Target
{
public void request(); //示意方法,客户端请求处理的方法
}
/**
* 适配器
*/
public class Adapter implements Target {
private Adaptee adaptee; //持有需要被适配的接口对象
public Adapter(Adaptee adaptee) { //构造方法,传人需要被适配的对象
this.adaptee = adaptee;
}
public void request() {
//可能转调已经实现了的方法,进行适配
adaptee.specificRequest();
}
}
public class Adaptee { //以及存在的接口
/**
* 示意方法,原本已经存在,已经实现的方法
*/
public void specificRequest() {
//具体的功能处理
}
}
public class Client {
public static void main(String[] args) {
//创建需被适配的对象
Adaptee adaptee = new Adaptee();
//创建客户端需要调用的接口对象
Target target = new Adapter(adaptee);
//请求处理
target.request();
}
}
9.4多重继承
采用接口的核心原因
1.表示可以向上转型多个基类型(有多种不同的行为,从此带来的灵活性)
2.和抽象类相同,防止客户端创建该类的对象,确保仅仅是建立一个接口.(接口和抽象类的选择 : 事实上如果知道某事物应该成为一个基类,第一反应应该使它成为一个接口)
9.6适配接口
接口一种常见用法就是策略模式.这使得方法更加灵活,通用,并具有复用性.
9.7接口中的域
在java 5以后enum很好的解决了定义一些常量的功能,所以在interface中定义常量的做法已经没有太大意义了.
在接口中可以定义域,但是不能定义空白final域.因为接口域隐式的是final staic ,而final 域必须在构造方法完成前初始化,然而接口是没有构造方法的,所以没有初始化的域将是一个没有意义的值.
域可以初始化 成表达式或者值,可以通过 new 对象获取值.
9.8嵌套接口
类中可以嵌套接口,接口中也可以嵌套接口.类中的嵌套接口是可以支持 public,protected,默认缺省(包内可见),private 权限.但是在接口中嵌套的接口只能是public/(缺省权限也是public)
在Class类中,如果出现一个方法 ”返回一个private接口的引用的public方法” 例如:
package tinking_in_java.interfacetest;
/**
* Created by leon on 17-12-15.
*/
public class Innerclass {
private interface D {
void f();
}
public class DImpl2 implements D {
@Override
public void f() {
System.out.println("DImpl2");
}
}
public D getD() {
return new DImpl2();
}
private D dRef;
public void setD(D d) {
dRef = d;
dRef.f();
}
}
package tinking_in_java.interfacetest;
/**
* Created by leon on 17-12-15.
*/
public class InnerTest {
public static void main(String[] args) {
Innerclass innerclass = new Innerclass();
// 除了在Innerclass 内部,其他地方无法获取到D引用,因为这个接口是private
//D d = innerclass.getD();
innerclass.setD(innerclass.getD());
}
}
//output-----
DImpl2
9.9接口与工厂
接口实现多重继承的途径的典型例子就是工厂方法.
总结:任何抽象性都应该是应真正的需求而产生的.恰当的原因是,优先选择类而不是接口,如果接口的必须性变得很明确了,那么就进行重构.
网友评论