

Kotlinで、あるインターフェースに関するいくつかの実装クラスのうちの一つのクラスだけ特定メソッドを呼び出したくないため、コンパイル時エラーとすることで使用を防ぎたいです。  
具体的には、次のようなインターフェースとクラス定義があったとします。

[code]

    interface IHoge{
      fun test()
    }
    class A: IHoge
    class B: IHoge
    class C: IHoge
    
[/code]

とあったときに`C.test()`は呼び出したくないとします。つまりここの状況下で`C.test()`と記述したらコンパイルエラーとなるようにしたいのですが、そのような方法はあるのでしょうか？

メソッドを

[code]

    fun test(){
      error()
    }
    
[/code]

などとすることによって実行時エラーを起こすことは簡単です。しかし、コンパイル時に`C.test()`を呼ぶことを防ぐ手段はあるのでしょうか？javaの例でも構いません。


CC BY-SA 4.0
2020-05-19T15:43:52.307
66783
2020-05-21T18:30:29.113
2020-05-19T16:07:37.497
40229
40229
question
java kotlin
Kotlinであるクラスのメソッドの呼び出しをコンパイル時エラーとして防ぎたい


`C.test()`を呼び出したくないのですから、そもそも`C`は`IHoge`を実装すべきではありません。以下`IHoge`の実装に手を出せる場合と、そうでない場合について回答してみます。

### 1.IHogeの実装を変更できる場合

この場合はインターフェースを分離するのが適当だと思います。

[code]

    interface IHoge {
      fun test()
    }
    
    interface IFuga {
      // 元のIHogeのtest()以外のメソッド
    }
    
    class A: IHoge, IFuga
    class B: IHoge, IFuga
    class C: IFuga
    
    
[/code]

### 2.IHogeの実装を変更できない場合

こちらの場合はインターフェースのデフォルト実装を使いたいという要求がありうるので、`test()`以外のメソッドを持つ`IPartial`を定義し、新しいクラス`D`で`IHoge`と`IPartial`を「マージ」するようにしています。  
さらに`C`に`D`を直接継承させず、[デリゲート](https://kotlinlang.org/docs/reference/delegation.html)することで、`IHoge`のサブクラスにならないようにしています。

[code]

    interface IHoge {
        fun f() = println("f()")
        fun test() = println("test()")
    }
    
    interface IPartial {
        fun f();
    }
    
    open class D : IHoge, IPartial {
        override fun f() = super<IHoge>.f();
    }
    
    class A : D()
    class B : D()
    
    class C(p: IPartial = D()) : IPartial by p {
    }
    
    fun main() {
        val l1 = mutableListOf<IHoge>(A(), B())
        // l1.add(C(A())) // <--- コンパイル時エラー
        l1.forEach { it.test() }
    
        val l2 = mutableListOf<IPartial>(A(), B(), C());
        l2.forEach { it.f() }
    }
    
[/code]

[code]

    test()
    test()
    f()
    f()
    f()
    
[/code]


CC BY-SA 4.0
2020-05-20T04:11:53.600
66801
2020-05-20T04:11:53.600
13199
66783
answer


本来の用途とは異なりますが、[`Deprecated`アノテーション](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/)が利用可能ではないでしょうか。

(Javaとは異なり)`level`に`DeprecationLevel.ERROR`を指定すればコンパイルエラーにできます。

注意点としては、`IHoge`経由で`test()`メソッドを呼び出した場合、実際のインスタンスが`C`のものであっても検知できません。

[code]

    interface IHoge {
        fun test()
    }
    
    class A : IHoge {
        override fun test() {}
    }
    
    class C : IHoge {
        @Deprecated(message = "利用不能メソッド", level = DeprecationLevel.ERROR)
        override fun test() {}
    }
    
    fun main(args: Array<String>) {
        val c = C()
        c.test() // コンパイルエラー
    
        val i: IHoge = C()
        i.test() // コンパイルエラーにならない
    }
    
[/code]

ということです。

* * *

ただ、私も質問文の内容を読む限りでは、他の方の回答と同様、設計が誤っているように感じます(ので、設計を見直すべきだと考えます)。


CC BY-SA 4.0
2020-05-20T19:05:23.523
66825
2020-05-21T18:30:29.113
2020-05-21T18:30:29.113
2808
2808
66783
answer