ホームページ > Java > &#&チュートリアル > Java の改善パート 4 -----抽象クラスとインターフェイス

Java の改善パート 4 -----抽象クラスとインターフェイス

黄舟
リリース: 2017-02-09 13:36:26
オリジナル
977 人が閲覧しました

インターフェースと内部クラスは、インターフェースを実装から分離するためのより構造化された方法を提供します。

抽象クラスとインターフェイスは、Java 言語で抽象概念を定義するための 2 つのメカニズムです。これらが存在するからこそ、Java には強力なオブジェクト指向機能が備わっています。抽象的な概念のサポートは非​​常に似ており、互換性さえありますが、違いもあります。


1. 抽象クラス

オブジェクト指向の分野では、すべてがオブジェクトであり、すべてのオブジェクトがクラスによって記述されることは誰もが知っていますが、オブジェクトの記述にすべてのクラスが使用されるわけではありません。クラスに特定のオブジェクトを説明するのに十分な情報がなく、それをサポートするために他の特定のクラスが必要な場合、そのようなクラスを抽象クラスと呼びます。たとえば、 new Animal() は、Animal オブジェクトを生成することは誰もが知っていますが、この Animal が具体的にどのようなものであるかはわかりません。そのため、これは必要な抽象クラスです。犬や猫などの特定の動物がどのようなものであるかを知ることができます。

オブジェクト指向の分野では、抽象概念には対応する問題領域の具体概念がないため、抽象概念を表現するための抽象クラスをインスタンス化することができない。

同時に、抽象クラスはデータ抽象化のアイデアを具体化し、ポリモーフィズムを実現するメカニズムです。これは抽象メソッドのセットを定義し、この抽象メソッドのセットの特定の式は派生クラスによって実装できます。同時に、抽象クラスは継承の概念を提供します。その出発点は継承です。そうでなければ存在の意味がありません。したがって、定義された抽象クラスは継承に使用される必要があります。同時に、抽象クラスをノードとする継承関係の階層チェーンでは、リーフ ノードが特定の実装クラスである必要があります。 (この理解が間違っているかどうかはわかりません!!! 専門家の指導...)

抽象クラスを使用する際に注意すべき点がいくつかあります:

1. 抽象クラスはインスタンス化できません。インスタンス化の作業は、完了はサブクラスに任せます。必要なのは参照だけです。

2. 抽象メソッドはサブクラスによって書き換える必要があります。

3. 抽象クラスに抽象メソッドが含まれている限り、他のメソッドが含まれているかどうかに関係なく、そのメソッドは抽象クラスとして定義する必要があります。

4. 抽象クラスには特定のメソッドを含めることができますが、もちろん抽象メソッドを含める必要はありません。

5. サブクラスの抽象メソッドは、親クラスの抽象メソッドと同じ名前にすることはできません。

6. Abstract は、final と並行して同じクラスを変更することはできません。

7. Abstract を使用して、private、static、final、またはネイティブと並行して同じメソッドを変更することはできません。 ,

抽象的な動物クラス Animal を定義し、cry( )、猫と犬は動物クラスのサブクラスです。cry() は抽象メソッドであるため、Cat と Dog は cry() )メソッドを実装する必要があります。以下のように:

public abstract class Animal {  
    public abstract void cry();  
}  
  
public class Cat extends Animal{  
  
    @Override  
    public void cry() {  
        System.out.println("猫叫:喵喵...");  
    }  
}  
  
public class Dog extends Animal{  
  
    @Override  
    public void cry() {  
        System.out.println("狗叫:汪汪...");  
    }  
  
}  
  
public class Test {  
  
    public static void main(String[] args) {  
        Animal a1 = new Cat();  
        Animal a2 = new Dog();  
          
        a1.cry();  
        a2.cry();  
    }  
}  
  
--------------------------------------------------------------------  
Output:  
猫叫:喵喵...  
狗叫:汪汪...
ログイン後にコピー

抽象クラスと抽象メソッドを作成すると、クラスの抽象化が明示的になり、ユーザーとコンパイラにその使用方法が指示されるため、非常に便利です。また、抽象クラスを使用すると、移動が簡単になるため、便利なリファクタラーでもあります。 public メソッドは継承階層の上にあります。 (From:Think in java)


2. インターフェース

インターフェースは、抽象クラスよりもさらに抽象的な「クラス」です。これより適切に表現できる言葉が見つからないため、ここでは「クラス」を引用符で囲みましたが、インターフェイス自体はクラスではないことを明確にする必要があります。これは、インターフェイスをインスタンス化できないという事実からもわかります。たとえば、 new Runnable(); は、その実装クラスを new することしかできません。 Ω インターフェイスは、クラス間のプロトコルを確立するために使用されます。これは、特定の実装を持たずに形式を提供するだけです。同時に、インターフェイスを実装する実装クラスは、インターフェイスのすべてのメソッドを実装する必要があります。implements キーワードを使用すると、クラスが特定のインターフェイスまたは特定のインターフェイスのグループに従っていることも示されます。外観はまだわかりませんが、どのように機能するかを説明する必要があります。」

接口是抽象类的延伸,java了保证数据安全是不能多重继承的,也就是说继承只能存在一个父类,但是接口不同,一个类可以同时实现多个接口,不管这些接口之间有没有关系,所以接口弥补了抽象类不能多重继承的缺陷,但是推荐继承和接口共同使用,因为这样既可以保证数据安全性又可以实现多重继承。

在使用接口过程中需要注意如下几个问题:

1、个Interface的方所有法访问权限自动被声明为public。确切的说只能为public,当然你可以显示的声明为protected、private,但是编译会出错!

2、接口中可以定义“成员变量”,或者说是不可变的常量,因为接口中的“成员变量”会自动变为为public static final。可以通过类命名直接访问:ImplementClass.name。

3、接口中不存在实现的方法。

4、实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。

5、不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可以使用 instanceof 检查一个对象是否实现了某个特定的接口。例如:if(anObject instanceof Comparable){}。

6、在实现多接口的时候一定要避免方法名的重复。


三、抽象类与接口的区别

尽管抽象类和接口之间存在较大的相同点,甚至有时候还可以互换,但这样并不能弥补他们之间的差异之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。

3.1语法层次

在语法层次,java语言对于抽象类和接口分别给出了不同的定义。下面已Demo类来说明他们之间的不同之处。

使用抽象类来实现:

public abstract class Demo {  
    abstract void method1();  
      
      
    void method2(){  
        //实现  
    }  
}
ログイン後にコピー

使用接口来实现

interface Demo {  
    void method1();  
    void method2();  
}
ログイン後にコピー



抽象类方式中,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。在某种程度上来说,接口是抽象类的特殊化。


对子类而言,它只能继承一个抽象类(这是java为了数据安全而考虑的),但是却可以实现多个接口。

3.2设计层次

上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,我们就必须要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。一般来说他们存在如下三个不同点:

1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。

3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。

(上面纯属个人见解,如有出入、错误之处,望各位指点!!!!)

为了更好的阐述他们之间的区别,下面将使用一个例子来说明。该例子引自:http://blog.csdn.net/ttgjz/article/details/2960451

我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:

抽象类:

abstract class Door{  
    abstract void open();  
    abstract void close();  
}
ログイン後にコピー

接口

interface Door{  
    void open();  
    void close();  
}
ログイン後にコピー

至于其他的具体类可以通过使用extends使用抽象类方式定义Door或者Implements使用接口方式定义Door,这里发现两者并没有什么很大的差异。

但是现在如果我们需要门具有报警的功能,那么该如何实现呢?

解决方案一:给Door增加一个报警方法:clarm();

abstract class Door{  
    abstract void open();  
    abstract void close();  
    abstract void alarm();  
}
ログイン後にコピー

或者

interface Door{  
    void open();  
    void close();  
    void alarm();  
}
ログイン後にコピー

这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)—见批注,在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变而改变,反之依然。


解决方案二

既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种:

1、两个都使用抽象类来定义。

2、两个都使用接口来定义。

3、一个使用抽象类定义,一个是用接口定义。

由于java不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。

如果选择第二种都是接口来定义,那么就反映了两个问题:1、我们可能没有理解清楚问题域,AlarmDoor在概念本质上到底是门还报警器。2、如果我们对问题域的理解没有问题,比如我们在分析时确定了AlarmDoor在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。

第三种,如果我们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor本质是们,所以对于这个概念我们使用抽象类来定义,同时AlarmDoor具备报警功能,说明它能够完成报警概念中定义的行为功能,所以alarm可以使用接口来进行定义。如下:

abstract class Door{  
    abstract void open();  
    abstract void close();  
}  
  
interface Alarm{  
    void alarm();  
}  
  
class AlarmDoor extends Door implements Alarm{  
    void open(){}  
    void close(){}  
    void alarm(){}  
}
ログイン後にコピー

这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。

批注:

   ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。

   一个类对另外一个类的依赖性应当是建立在最小的接口上的。

インターフェースは役割を表し、異なる役割を 1 つのインターフェースに割り当てないでください。関連性のないインターフェイスが結合されて、大きく肥大化したインターフェイスが形成され、ロールとインターフェイスが汚染されます。

IV. 概要

1. 抽象クラスは Java 言語の継承関係を表しますが、サブクラスは親クラスを 1 つだけ持つことができますが、複数のインターフェイスを持つことができます。

2. 抽象クラスは独自のメンバー変数と非抽象クラス メソッドを持つことができますが、インターフェイス内に存在できるのは静的かつ不変のメンバー データのみです (ただし、メンバー データは通常インターフェイス内で定義されていません)。また、そのすべてのメソッドはすべて抽象的。

3. 抽象クラスとインターフェイスによって反映される設計概念は異なります。抽象クラスは「である」関係を表し、インターフェイスは「似た」関係を表します。

抽象クラスとインターフェースは Java 言語における 2 つの異なる抽象概念ですが、それらの間には大きな類似点がありますが、その存在によりポリモーフィズムが非常によくサポートされます。しかし、彼らの選択は多くの場合、問題領域に対するあなたの理解を反映しています。問題領域の性質をよく理解して初めて、正しく合理的な設計を行うことができます。

基礎を固め、スキルを向上させ、困難を恐れず、頂上まで登りましょう! ! ! ! ! !


上記は Java 改善パート 4 の内容です-----抽象クラスとインターフェイス さらに関連した内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。


ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート