9 接口
接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
9.1 抽象类和抽象方法
抽象方法:仅有声明而没有方法体
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须限定为抽象的(否则,编译会报错),抽象类的方法不一定全为抽象方法。
若从一个抽象类继承,想创建该类的对象,那么必须重写基类的方法。若不这样做导出类也会为抽象类,编译器会强制用abstract限定。
public enum Note {
MIDDLE_C
}
public abstract class Instrument {
private int i;
public abstract void play(Note note);
public String what(){
return "instrument";
}
public abstract void adjust();
}
public class Wind extends Instrument {
@Override
public void play(Note note) {
System.out.println("Wind.paly()"+note);
}
public String what(){
return "Wind";
}
@Override
public void adjust() {
}
}
public class Percussion extends Instrument {
@Override
public void play(Note note) {
System.out.println("Percussion.play()"+note);
}
public String what(){
return "Percussion";
}
@Override
public void adjust() {
}
}
public class Brass extends Wind {
public void play(Note note) {
System.out.println("Brass.play()"+note);
}
public String what(){
return "Brass";
}
public void adjust() {
}
}
public class Music {
static void tune(Instrument i){
i.play(Note.MIDDLE_C);
}
static void tuneAll(Instrument[] e){
for (Instrument a:e){
tune(a);
}
}
public static void main(String[] args) {
Instrument[] o = {new Wind(),new Percussion(),new Brass()};
tuneAll(o);
}
}
//OUTPUT
Wind.paly()MIDDLE_C
Percussion.play()MIDDLE_C
Brass.play()MIDDLE_C
练习1:修改第8章练习9中的Rodent,使其成为一个抽象类。只要有可能,就将Rodent的方法声明为抽象方法。
public abstract class Rodent {
public abstract void bit();
}
public class Mouse extends Rodent {
@Override
public void bit() {
System.out.println("Mousebit.....");
}
}
public class Gerbil extends Rodent {
@Override
public void bit() {
System.out.println("Gerbilbit......");
}
}
练习2:创建一个不包含任何抽象方法的抽象类,并验证我们不能为该类创建任何实例。
public abstract class Test2 {
}
public class Test {
public static void main(String[] args) {
new Test2();//error 如下图
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGjj7iqm-1600073515437)(C:\Users\srqnk\AppData\Roaming\Typora\typora-user-images\1592464991100.png)]
练习3:创建一个基类,让它包含抽象方法print(),并在导出类中覆盖该方法。覆盖后的方法版本可以打印导出类中定义的某个整型变量的值。在定义该变量之处,赋予它非0值。在基类的构造器中调用这个方法。现在,在main()方法中,创建一个导出类对象,然后调用它的print()方法请解释发生的情形。
public abstract class Test3 {
abstract void print();
Test3(){
print();
}
}
public class Test3Son extends Test3 {
int i = 6;
@Override
void print() {
System.out.println("Test3Son"+i);
}
}
public class Test {
public static void main(String[] args) {
new Test3Son();
}
}
//output(出现这种结果是因为初始化的顺序,先加载的父类,子类的变量还没来得及初始化所以为0 )
Test3Son0
练习4:创建一个不包含任何方法的抽象类,从它那里导出一个类,并添加一个方法创建一个静态方法,它可以接受指向基类的引用,将其向下转型到导出类,然后再调用该静态方法。在main()中展现它的运行情况。然后,为基类中的方法加上abstract声明,这样就不再需要进行向下转型。
public abstract class Test4 {
}
public class Test4Son extends Test4 {
void service(){
System.out.println("Test4Sonservice");
}
public static void ss(Test4 test4){
System.out.println("staticss");
((Test4Son)test4).service();//向下转型
}
public static void main(String[] args) {
Test4 t = new Test4Son();
ss(t);
}
}
9.2 接口
interface产生了一个完全抽象的类,允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,而没有提供任何具体的实现
它允许人们通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。
接口默认访问权限在包内;接口可以包含域,但都隐式的是static和final;方法,默认public
要让一个类遵循某个特定接口需要使用implements关键字
练习5:在某个包内创建一个接口,内含三个方法,然后在另一个包中实现此接口
public interface Instrument {
void s1();
void s2();
void s3();
}
public class Test5 implements Instrument {
@Override
public void s1() {
System.out.println("s1");
}
@Override
public void s2() {
System.out.println("s2");
}
@Override
public void s3() {
System.out.println("s3");
}
}
练习6:证明接口内所有的方法都自动是public
在另一个包内新建一个类实现接口能重写方法即为public
练习7:修改第八章中的练习8是Rodent成为一个接口
public interface Rodent {
void bit();
}
public class Gerbil implements Rodent {
@Override
public void bit() {
System.out.println("Gerbil...");
}
}
public class Mouse implements Rodent {
@Override
public void bit() {
System.out.println("Mouse");
}
}
练习8:在polymorphism.Sandwich.java中,创建接口FastFood并添加加合适的方法,然后修改Sandwich以实现FastFood接口。
练习9:重构Music5.java,将在Wind,Precussion和Stringed中的公共方法移入一个抽象类中。
与上类似
练习10:修改Music5.java,添加Playable接口。将play()的声明从Instrument中移动Playable中。通过将Playable包括在implements列表中,把Playable添加到导出类中。修改tune()使它接受Playable而不是Instrument作为参数。
enum Note {
MIDDLE_C, C_SHARP, B_FLAT;
}
interface Instrument {
String toString();
void adjust();
}
interface Playable{
void play(Note n) ;
}
class Wind implements Instrument,Playable {
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
public String toString() {
return "Wind";
}
public void adjust() {
System.out.println("Adjusting Wind");
}
}
class Percussion implements Instrument,Playable {
public void play(Note n) {
System.out.println("Percussion.play() " + n);
}
public String toString() {
return "Percussion";
}
public void adjust() {
System.out.println("Adjusting Percussion");
}
}
class Stringed implements Instrument,Playable {
public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
public String toString() {
return "Stringed";
}
public void adjust() {
System.out.println("Adjusting Stringed");
}
}
class Brass extends Wind {
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
public void adjust() {
System.out.println("Adjusting Brass");
}
}
class Woodwind extends Wind {
public void play(Note n) {
System.out.println("Woodwind.play() " + n);
}
public String toString() {
return "Woodwind";
}
}
public class Main {
public static void tune(Playable i) {
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Playable[] e) {
for (Playable i : e) {
tune(i);
System.out.println(i);
}
}
public static void main(String[] args) {
// Upcasting during addition to the array:
Playable[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
9.3 完全解耦
public class Processor {
public String name(){
return getClass().getSimpleName();
}
Object process(Object input){return input;}
}
public class Upcase extends Processor {
String process(Object input){
return ((String)input).toUpperCase();//大写
}
}
public class Downcase extends Processor {
String process(Object input){
return ((String)input).toLowerCase();//小写
}
}
public class Splitter extends Processor {
String process(Object input){
return Arrays.toString(((String)input).split(" "));//以传递进来的参数作为边界,将String对象分割开,然后返回一个数组String[]
}
}
public class Apply {
public static void process(Processor p,Object s){
System.out.println("Using Processor "+p.name());
System.out.println(p.process(s));
}
public static String s = "Disagreement with beliefs is by definition incorrect";
public static void main(String[] args) {
process(new Upcase(),s);
process(new Downcase(),s);
process(new Splitter(),s);
}
}
//output
Using Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Processor Downcase
disagreement with beliefs is by definition incorrect
Using Processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrect]
Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上。
像这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。策略就是传递进去的参数对象,它包含要执行的代码。在本例中Processor对象就是一个策略,在main()中可以看到有三种不同类型的策略应用到String类型的s对象上。
public interface Processor {
String name();
Object process(Object input);
}
public class Waveform {
private static long counter;
private final long id = counter++;
public String toString(){
return "Waveform" + id;
}
}
public class LowPass extends Filter {
double cutoff;
public LowPass(double cutoff){this.cutoff = cutoff;}
public Waveform process(Waveform input){
return input;
}
}
public class HighPass extends Filter {
double cutoff;
public HighPass(double cutoff){this.cutoff = cutoff;}
public Waveform process(Waveform input){
return input;
}
}
public class BandPass extends Filter {
double lowsCutoff,highCutoff;
public BandPass(double lowsCut,double highCut){
lowsCutoff = lowsCut;
highCutoff = lowsCut;
}
public Waveform process(Waveform input){return input;}
}
public class Filter {
public String name(){
return getClass().getSimpleName();
}
public Waveform process(Waveform input){
return input;
}
}
public class FilterAdapter implements Processor {
Filter filter;
public FilterAdapter(Filter filter){
this.filter = filter;
}
@Override
public String name() {
return filter.name();
}
@Override
public Waveform process(Object input) {
return filter.process((Waveform) input);
}
}
public class Apply {
public static void process(Processor p,Object s){
System.out.println("Using Processor "+p.name());
System.out.println(p.process(s));
}
}
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new LowPass(1.0)), w);
Apply.process(new FilterAdapter(new HighPass(2.0)),w);
Apply.process(new FilterAdapter(new BandPass(3.0,4.0)),w);
}
}
//output
Using Processor LowPass
Waveform0
Using Processor HighPass
Waveform0
Using Processor BandPass
Waveform0
上例,FilterAdapter的构造器接受你所拥有的接口Filter,然后生成具有你所需要的Processor接口的对象。在FilterAdapter类中用到了代理,将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也更具可复用性。适配器模式接受你所拥有的接口并产生你所需要的接口。
练习11:创建一个类,它有一个方法用于接受一个String类型的参数,生成的结果是将该参数中每一对字符进行互换。对该类进行适配,使得它可以用于interfaceprocessor.Apply.process()。
public interface Processor {
String name();
Object process(Object input);
}
public class Test11 {
/* public String process(String str){
char[] charArray = str.toCharArray();
for (int i = 0; i < charArray.length; i += 2) {
char temp = charArray[i];
charArray[i] = charArray[i + 1];
charArray[i + 1] = temp;
}
return new String(charArray);
}*/
static String swap(String s) {
StringBuilder sb = new StringBuilder(s);
for (int i = 0; i < sb.length() - 1; i += 2) {
char c1 = sb.charAt(i);
char c2 = sb.charAt(i + 1);
sb.setCharAt(i, c2);
sb.setCharAt(i + 1, c1);
}
return sb.toString();
}
public String name() {
return getClass().getSimpleName();
}
public static void main(String[] args) {
Apply.process(new SwapCharactersAdapter(),"1234");
}
}
public class SwapCharactersAdapter implements Processor {
@Override
public String name() {
return new Test11().name();
}
@Override
public Object process(Object input) {
//return new Test11().process((String)input);
return Test11.swap((String)input);
}
}
public class Apply {
public static void process(Processor p, Object s) {
System.out.println("Using Processor " + p.name());
System.out.println(p.process(s));
}
}
//output
Using Processor Test11
2143
9.4 java中的多继承
继承只能单继承;接口可以实现implements多个,用逗号一一隔开
extends … implements …,…,…
public interface CanFight {
void fight();
}
public interface CanSwim {
void swim();
}
public interface CanFly {
void fly();
}
public class ActionCharacter {
public void fight(){
}
}
public class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly {
@Override
public void fly() {
}
@Override
public void swim() {
}
}
public class Adventure {
public static void t(CanFight x){x.fight();}
public static void u(CanSwim x){x.swim();}
public static void v(CanFly x){x.fly();}
public static void w(ActionCharacter x){x.fight();}
public static void main(String[] args) {
Hero hero = new Hero();
Adventure.t(hero);
Adventure.u(hero);
Adventure.v(hero);
Adventure.w(hero);
}
}
例子展示的使用接口的原因:
1.为了能够向上转型为多个基类型(带来了灵活性)
2.防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。
练习12:在Adventure.java中,按照其他接口的样式,增加一个CanClimb接口。
public interface CanZou {
void zou();
}
public class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly,CanZou {
@Override
public void fly() {
}
@Override
public void swim() {
}
@Override
public void zou() {
}
}
public class Adventure {
public static void t(CanFight x){x.fight();}
public static void u(CanSwim x){x.swim();}
public static void v(CanFly x){x.fly();}
public static void w(ActionCharacter x){x.fight();}
public static void z(CanZou x){x.zou();}
public static void main(String[] args) {
Hero hero = new Hero();
Adventure.t(hero);
Adventure.u(hero);
Adventure.v(hero);
Adventure.w(hero);
Adventure.z(hero);
}
}
练习13:创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。
9.5 通过继承来扩展接口
接口之间可以实现extends继承一个或多个接口,当类实现接口的时候必须全部重写里边的方法
练习14:创建三个接口,每个接口包含两个方法。继承出一个接口,它组合了这三个接口并添加了一个新方法。创建一个实现了该类接口并且继承了某个具体的类的类。现在编写四个方法,每一个都接受这四个接口之一作为参数。在main()方法中,创建这个类的对象,并将其传递给这四个方法。
public interface First {
void doFirst();
}
public interface Second {
void doSecond();
}
public interface Third {
void doThird();
}
public interface All extends First,Second,Third {
void doAll();
}
public class Test implements All {
public void doTest1(First first){
System.out.println("doFirst");
}
public void doTest2(Second second){
System.out.println("doSecond");
}
public void doTest3(Third third){
System.out.println("doThird");
}
public void doTestAll(All all){
System.out.println("doAll");
}
@Override
public void doAll() {
}
@Override
public void doFirst() {
}
@Override
public void doSecond() {
}
@Override
public void doThird() {
}
public static void main(String[] args) {
Test t = new Test();
t.doTest1(t);
t.doTest2(t);
t.doTest3(t);
t.doTestAll(t);
}
}
练习15:将前一个练习修改为:创建一个抽象类,并将其继承到一个导出类中。
9.5.1 组合接口时的名字冲突
不同接口避免设置相同方法名。
9.6 适配接口
你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口。
Scanner类(获取用户输入的一个类),它接受的是一个readable接口
9.7 接口中的域
自动都是static和final,对于常量的定义一般为大写字母用下划线分割多个字符
9.7.1 初始化接口中的域
在接口中定义的域不能是空final,但可以被非常量的表达式初始化。
9.8 嵌套接口
接口可以嵌套在类中或其它接口中,接口可被实现为private,可以强制该接口中的方法定义不要添加任何类型信息(不允许向上转型)
class A{
interface B{
void f();
}
}
interface C{
interface D{
void f();
}
}
9.9 接口与工厂
接口是实现多重继承的途径,而生成遵循接口的对象的典型方式就是工厂方法设计模式
public interface Service {
void method1();
void method2();
}
public interface ServiceFactory {
Service getService();
}
public class Implementation1 implements Service {
@Override
public void method1() {
System.out.println("Implementation1 method1");
}
@Override
public void method2() {
System.out.println("Implementation1 method2");
}
}
public class Implementation1Factory implements ServiceFactory {
@Override
public Service getService() {
return new Implementation1();
}
}
public class Implementation2 implements Service {
@Override
public void method1() {
System.out.println("Implementation2 method1");
}
@Override
public void method2() {
System.out.println("Implementation2 method2");
}
}
public class Implementation2Factory implements ServiceFactory {
@Override
public Service getService() {
return new Implementation2();
}
}
//output
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
on1 method2");
}
}
public class Implementation1Factory implements ServiceFactory {
@Override
public Service getService() {
return new Implementation1();
}
}
public class Implementation2 implements Service {
@Override
public void method1() {
System.out.println(“Implementation2 method1”);
}
@Override
public void method2() {
System.out.println("Implementation2 method2");
}
}
public class Implementation2Factory implements ServiceFactory {
@Override
public Service getService() {
return new Implementation2();
}
}
//output
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2