2017-02-11 7 views
4

私は以前に書いたクラスを拡張することを含むいくつかのコードを書いています。ファイルは名前と型のサイズを取るコンストラクタ長いです。元のクラスでは、コンストラクタ内で、入力されたファイル名に "。"が1つ含まれていることを確認しました。ファイル上の特定の拡張子を必要としませんでした。私が書いているこの新しいクラスでは、名前の拡張子を ".mp3"にする必要があります。しかし、私のコンパイラは、スーパーコンストラクタの前に検証が嫌いです。スーパーコンストラクタを呼び出す前に変数の妥当性をチェックする

これは私の現在のコードです:

public class Song extends DigitalMedia{ 

private String artist; 
private String album; 
private String name; 
private long size; 

public Song(String aName, long aSize, String aArtist, String aAlbum){ 
    super(aName, aSize); 
    setArtist(aArtist); 
    setAlbum(aAlbum); 
} 

は、私はそのコンストラクタを作成する前にプール「aName」は「.MP3" が含まれていることを確認する方法はありますか?

+0

@J Zaneコンストラクタの呼び出し階層について勉強し、スーパークラスコンストラクタを最初に呼び出すことが必須であるように設計されている理由を勉強することをお勧めします。私たちが勉強するときには、事実を消化するだけではなく、そのような理由で答えを見つけようとするべきです。そうでなければ何が起こったのでしょうか。また、コンストラクタから例外がスローされた場合に何が起こるかを調べることをお勧めします。私は私の答えでこれらのことに少しを置こうとしました。http://stackoverflow.com/a/42179930/504133 –

答えて

3

私はそれはあなたのプログラムを設計するための最良の方法だかどうかを言うことはできませんが、super引数の1の内側にバリデータメソッドを呼び出すことができます。

public Song(String aName, long aSize, String aArtist, String aAlbum){ 
    super(validateName(aName), aSize); 
    setArtist(aArtist); 
    setAlbum(aAlbum); 
} 

private static String validateName(String name) { 
    if (whatever) { 
     throw new Whatever(); 
    } 
    return name; 
} 
1

代替ソリューションを介して、あなたのルールを強制することです組み込み型チェック。

あなたは作成できMediaFormat

interface MediaFormat { } 

あなたは音楽の形式がサポートされているかを指定することができ、MediaFormatを実装していMusicFormat

enum MusicFormat implements MediaFormat { 
    MP3("mp3"); 

    private final String format; 

    MusicFormat(String format) { 
     this.format = format; 
    } 

    @Override 
    public String toString() { 
     return format; 
    } 
} 
DigitalMediaが、その後 MediaFormatで構成することができ

class DigitalMedia { 
    private final MediaFormat format; 
    private final String name; 

    public DigitalMedia(String name, MediaFormat format) { 
     this.name = name; 
     this.format = format; 
    } 
} 

Songは受け入れることができるMusicFormat:これはすべてのそれらの厄介なチェックを避け、MusicFormatに指定されているものを使用することをユーザに強制します

class Song { 
    public Song(String name, MusicFormat format) { 
     super(name, format); 
    } 
} 

name + "." + format

+0

私はこれが最良の選択だと思います。厳密な型指定は、コンパイル時にエラーに対処する可能性を提供しますが、他のソリューションでは実行時にエラーを処理する必要があります。これはしばしば難しく、コードが複雑になります。もちろん、 'MusicFormat'が文字列から作成された場合、実行時エラー処理も必要ですが、この解決策では、その問題を曲作成から分離することができます。 –

+0

@MickMnemonicこれは定数です。ランタイムチェックは必要ありません。唯一の問題は、定数が間違って宣言されている場合です。実行時のチェックは役に立ちません。あなたは詳しく説明できますか? –

+1

OPがファイル拡張子をどこかの入力として受け取っているように見えるので、 'MusicFormat.valueOf(inputString)'は実行時に処理する必要があります。 –

0

継承スタンドポイントから、サブクラスは実際にはスーパークラスよりも制限が厳しいべきではありません。

しかし、サブクラスのインスタンス化をより制限したい場合は、コンストラクタprivateを作成して、検証を最初に行うファクトリメソッドを提供できます。

public class Song extends DigitalMedia { 

    private String artist; 
    private String album; 
    private String name; 
    private long size; 

    private Song(String aName, long aSize, String aArtist, String aAlbum) { 
     super(aName, aSize); 
     setArtist(aArtist); 
     setAlbum(aAlbum); 
    } 

    public static Song makeSong(String aName, long aSize, String aArtist, String aAlbum) { 
     //... validation code 
     return new Song(aName, aSize, aArtist, aAlbum); 
    } 
    ... 
} 

タイプ自体を制限する代わりに、カプセル化を使用して不変量を適用します。

2

コード実行がconstructorに達すると、オブジェクトはライブであり、現在はstates(フィールド)の初期化の準備が整っています。

クラスA.javaのオブジェクトは、スーパークラスのオブジェクトA.javaと呼ぶこともできます。 A.javaクラスが状態を初期化する前に、オブジェクトはスーパークラスのプロパティと機能を継承します。スーパークラスが初期化を行った後、クラスA.javaは初期化されます。

スーパークラスのコンストラクタは、スーパークラスにパラメータがないコンストラクタが存在する場合に暗黙的に呼び出されます。それ以外の場合は、スーパークラスのパラメータ化されたコンストラクタのいずれかを明示的に呼び出す必要があります。

constructorで条件が満たされない場合はどうしますか?例外をスローするオプションがありますが、オブジェクトがまだ作成されている場合は、finalize()メソッドをオーバーライドしてthisオブジェクトを確認することで同じことを確認できます。 finalize()メソッドにすぐに達するコード実行のためにSystem.gc()を呼び出すことによって、ガベージコレクタに影響を与えたいと思うかもしれません。

提案された解決策 コンストラクタを呼び出す前に、コンストラクタのパラメータを検証する必要があります。クラスにカプセル化したい場合は、非固有の静的メソッド(getInstance()のように名前を付けることもできます)を追加して、クラスSongのオブジェクトを作成して返します。そのような場合は、コンストラクタをプライベートとして持つことができます。これにより、クラスが拡張不可能になることに注意してください。これは単なる設計上の選択です。

関連する問題