接口和内部类为我们提供了一种将接口与实现分开的更加结构化的方法
在c++中这些概念只是间接支持,在java中存在关键字这个事实表明人们认为这些思想是很重要的,以至于要提供对它的直接支持。
还有另一个概念叫做抽象类,它是介于普通的类与接口之间的一个中庸之道。尽管在构造某些具有未实现方法的类时,你第一想到的可能是接口,但是抽象类仍旧是一种用于此目的的一种重要而必须的工具。因为你不可能总是使用纯接口。
**
一、 抽象类和抽象方法
**
abstract void f();
含上述抽象方法的类叫做抽象类,如果一个类中包含一个或多个抽象方法,那么这个类就必须被定义为抽象的,也就是抽象类。抽象类的关键字也是abstract。
package com.chenxyt.java.test;
abstract class Instrument{
private int i;
public String what(){
return "Instrumet";
}
public abstract void play(String s);
public void adjust(){};
}
class Wind extends Instrument{
public String what(){
return "Wind";
}
public void play(String s){
System.out.println("Wind.play" + s);
}
public void adjust(){};
}
class Percussion extends Instrument{
public String what(){
return "Percussion";
}
public void play(String s){
System.out.println("Percussion.play" + s);
}
public void adjust(){};
}
class Stringed extends Instrument{
public String what(){
return "Stringed";
}
public void play(String s){
System.out.println("Stringed.play" + s);
}
public void adjust(){};
}
class Brass extends Instrument{
public String what(){
return "Brass";
}
public void play(String s){
System.out.println("Brass.play" + s);
}
public void adjust(){};
}
class WoodWind extends Instrument{
public String what(){
return "WoodWind";
}
public void play(String s){
System.out.println("WoodWind.play" + s);
}
public void adjust(){};
}
public class Music{
static void tune(Instrument i ){
i.play("finish");
}
static void tuneAll(Instrument[] e){
for(Instrument i:e){
tune(i);
}
}
public static void main(String[] args) {
Instrument[] iArray = {
new Wind(),new Percussion(),new Brass(),new Stringed(),new WoodWind()
};
tuneAll(iArray);
}
}
创建抽象类和抽象方法非常有用,因为它们可以使类的抽象性明确起来,告诉用户和编译器打算怎么来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易的将公共方法沿着继承层次结构向上移动。
**
二 接口
**
interface关键字使抽象的概念更向前迈进了一步。
abstract关键字允许我们在类中创建一个没有方法实现的方法,方法的具体实现由继承它的子类创建。
interface关键字产生一个完全抽象的类,它根本就没有提供任何具体的实现,它允许创建者确定具体的方法名,参数列表和返回值,但是没有任何方法体,接口只提供了对外的形式,但是没有提供任何具体实现。
一个接口表示“所有实现了该特定接口的类看起来都像这样的”
package com.chenxyt.java.test;
interface Instrument{
public void play(String s);
}
class Wind implements Instrument{
public String what(){
return "Wind";
}
public void play(String s){
System.out.println("Wind.play" + s);
}
public void adjust(){};
}
class Percussion implements Instrument{
public String what(){
return "Percussion";
}
public void play(String s){
System.out.println("Percussion.play" + s);
}
public void adjust(){};
}
class Stringed implements Instrument{
public String what(){
return "Stringed";
}
public void play(String s){
System.out.println("Stringed.play" + s);
}
public void adjust(){};
}
class Brass implements Instrument{
public String what(){
return "Brass";
}
public void play(String s){
System.out.println("Brass.play" + s);
}
public void adjust(){};
}
class WoodWind implements Instrument{
public String what(){
return "WoodWind";
}
public void play(String s){
System.out.println("WoodWind.play" + s);
}
public void adjust(){};
}
public class Music{
static void tune(Instrument i ){
i.play("finish");
}
static void tuneAll(Instrument[] e){
for(Instrument i:e){
tune(i);
}
}
public static void main(String[] args) {
Instrument[] iArray = {
new Wind(),new Percussion(),new Brass(),new Stringed(),new WoodWind()
};
tuneAll(iArray);
}
}
**
三 完全解耦
**
只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想将这个方法应用在不在此继承结构中的某个类,那么你会触霉头了。那么使用接口将很大程度的放宽这种限制,因此,它可以使我们编写可复用性更好的代码。
package com.chenxyt.java.test;
import java.util.Arrays;
class Processor{
public String name(){
return getClass().getSimpleName();
}
Object process(Object input){
return input;
}
}
class UpCase extends Processor{
String process(Object input){
return input.toString().toUpperCase();
}
}
class DownCase extends Processor{
String process(Object input){
return input.toString().toLowerCase();
}
}
class Splitter extends Processor{
String process(Object input){
return Arrays.toString(input.toString().split(" "));
}
}
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 final 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);
}
}
Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像上面的案例一样,能够根据所传递的参数对象的不同而具有不同行为的方法称为策略模式。这类方法就是把不变得和变化的分开,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里的Processor对象就是一个策略,在main()中可以看到有三种不同类型的策略应用到string类型s对象上。
下面有如下4个类,他们看起来也适用Apply.process()
package com.chenxyt.java.practice;
public class Filter{
public String name(){
return getClass().getSimpleName();
}
public WaveForm process(WaveForm input){
return input;
}
}
Filter类,看上去与Processor类似,都有name()方法和process()方法,区别在于方法的参数类型和返回类型不同。
package com.chenxyt.java.practice;
public class HighPass extends Filter{
double cutoff;
public HighPass(double cutoff){
this.cutoff = cutoff;
}
public WaveForm prcess(WaveForm input){
return input;
}
}
HighPass类,继承自Filter类
package com.chenxyt.java.practice;
public class BandPass extends Filter {
double lowCutoff,highCutoff;
public BandPass(double lowCutoff,double highCutoff){
this.lowCutoff = lowCutoff;
this.highCutoff = highCutoff;
}
public WaveForm process(WaveForm input){
return input;
}
}
BandPass类,继承自Filter类
package com.chenxyt.java.practice;
public class WaveForm{
private static long counter;
private final long id = counter++;
public String toString(){
return "Wave Form" + id;
}
}
Filter与Processor类具有相同的内部接口元素(两个类的方法名都相同),但是由于Filter类并非继承自Processor类,因此当Apply.process()方法传入参数Filter的时候,由于Filter类的创建者并不知道要当做Processor类使用,并且它也不能通过向上转型的方式变成Processor类,因此不能将Filter类应用到Apply.process()方法。这主要是因为Apply.process()方法和Processor类的耦合度太高了,已经超出了所需要的程度。这就是Apply.process()方法只能接收Processor类或者其子类,而面对新的类的时候,Apply.process()方法就无能为力了,对其的复用也就被禁止了。
但是,正如前文所说,如果操作的是接口而不是类的时候,那么这些限制就会变得松动,使得你可以复用接口的Apply.process()方法,下面是修改为接口的版本。
package com.chenxyt.java.practice;
public interface Processor{
String name();
Object process (Object input);
}
此时Processor类变成了一个接口,复用代码的形式就是之前继承它的类,可以改为实现它的接口,并且Filter类,也可以编程实现Processor类的接口,这样Apply.process()方法的耦合度就降低了,并且支持了其它的类型。还有一种情况,假如一个类是被发现的,而不是被我们自己创建的,那么这个类就无法实现Processor接口,比如说,如果Filter类是在类库中的类,那么这个类就无法主动实现Processor接口,这时候可以使用适配器模式,在这个类的外部封装一层,作为适配器来实现要实现的接口。
如下:
package com.chenxyt.java.practice;
class FilterAdapter implements Processor{
Filter filter;
public FilterAdapter(Filter filter){
this.filter = filter;
}
public String name(){
return filter.name();
}
public WaveForm process(Object input){
return filter.process((WaveForm)input)
}
}
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);
}
**
四 Java中的多重继承
**
c++中允许多重继承,并且每一个继承的类都可以有一个实现,Java中可以实现多个接口,每个接口名字在implements后边用逗号隔开。
package com.chenxyt.java.practice;
interface CanFight{
void Fight();
}
interface CanSwim{
void Swim();
}
interface CanFly{
void Fly();
}
class ActionChracter{
public void Fight(){
//---
};
}
class Hero extends ActionChracter implements CanFight,CanSwim,CanFly{
public void Fly(){
//---
};
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(ActionChracter x){
x.Fight();
}
public static void main(String[] args) {
Hero h = new Hero();
t(h);
u(h);
v(h);
w(h);
}
}
**
五 通过继承来扩展接口
**
**
六适配接口
**
**
七 接口中的域
**
在接口中的域,会被自动的隐式转换为static final类型,所以接口就可以很便捷的创建一组常量值,也就是枚举。在JavaSE5之前,没有枚举的概念之前,可以使用接口来创建常量组。
package com.chenxyt.java.practice;
public interface Months{
int JANUARY = 1,FEBRUARY=2,MARCH=3,APRIL=4,
MAY=5,JUNE=6,JULY=7,AUGUST=8,SEPTEMBER=9,OCTOBER=10,
NOVEMBER=11,DECEMBER=12;
}
**
八 嵌套接口
**
package com.chenxyt.java.practice;
class A{
interface B{
void f();
}
public class BImp implements B{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
public class BImp2 implements B{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
public interface C{
void f();
}
class CImp implements C{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
private class CImp2 implements C{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
private interface D{
void f();
}
private class DImp implements D{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
private class DImp2 implements D{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
public D getD(){
return new DImp();
}
private D dRef;
public void reveiveD(D d){
dRef = d;
dRef.f();
}
}
interface E{
interface G{
void f();
}
public interface H{
void f();
}
void g();
//强制必须为public
//private interface I{};
}
public class NestingInterfaces{
public class BImp implements A.B{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
class CImp implements A.C{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
//因为接口D是私有的 所以不能被实现
//class DImp implements A.D{);
class EImp implements E{
@Override
public void g() {
// TODO Auto-generated method stub
}
}
class EGImp implements E.G{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
class EImp2 implements E{
@Override
public void g() {
// TODO Auto-generated method stub
}
class EG implements E.G{
@Override
public void f() {
// TODO Auto-generated method stub
}
}
}
public static void main(String[] args) {
A a = new A();
//D是private 不能实例化
//A.D ad = new A.D();
//getD()方法只能返回D
//A.DImp2 di2 = a.getD();
//private接口的域不能被访问
//a.getD().f();
//可以通过内部返回域的方法获取
A a2 = new A();
a2.reveiveD(a.getD());
}
}
**
九、接口与工厂
**
接口是实现多重继承的重要途径,而生成遵循某个接口对象的典型方式就是工厂方法设计模式。由此可见设计模式的重要性,我自己最近也在学习这一块的内容。希望能够有所提高。使用工厂方法与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上来说,我们通过这种方式可以将我们的代码与接口的实现完全分离,这就使我们可以透明的将某个实现替换成另一个实现。如下示例:
package com.chenxyt.java.practice;
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementation1 implements Service{
Implementation1() {
// TODO Auto-generated constructor stub
}
@Override
public void method1() {
// TODO Auto-generated method stub
System.out.println("Implementation1 method1");
}
@Override
public void method2() {
// TODO Auto-generated method stub
System.out.println("Implementation1 method2");
}
}
class Implementation1Factory implements ServiceFactory{
@Override
public Service getService() {
// TODO Auto-generated method stub
return new Implementation1();
}
}
class Implementation2 implements Service{
Implementation2() {
// TODO Auto-generated constructor stub
}
@Override
public void method1() {
// TODO Auto-generated method stub
System.out.println("Implementation2 method1");
}
@Override
public void method2() {
// TODO Auto-generated method stub
System.out.println("Implementation2 method2");
}
}
class Implementation2Factory implements ServiceFactory{
@Override
public Service getService() {
// TODO Auto-generated method stub
return new Implementation2();
}
}
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 Implementation1Factory());
serviceConsumer(new Implementation2Factory());
}
}
总结
抽象类和接口都是将具体方法更加抽象的一种形式。任何抽象性都应该是应真正的需求产生的。