Jestem weteranem Java, który uczy się Kotlina. Znalazłem się trochę mistyfikowani o tym, jak wziąć parametry konstruktora dostarczone klasie dzieci i przekształcić je do parametrów wymaganych przez klasę nadrzędną. Problem pojawia się, gdy parametry dostarczone dziecku nie są odpowiednie dla rodzica.

Na przykład, kiedy mam pomysł Intellij, konwertują następujące czynności do Kotlin ...

class Base
{
    final int w;
    final int h;

    Base(int w, int h)
    {
        this.w = w;
        this.h = h;
    }
}

class Derived extends Base
{
    Derived(int x)
    {
        // some complex derivation
        Converter c = new Converter(x);
        super(c.a, c.b);
    }
}

class Converter
{
    final int a;
    final int b;

    Converter(int x)
    {
        a = x + 2;
        b = x - 2;
    }
}

Dostaję następujące elementy, w tym błąd w wskazanej pozycji, mówiąc, że parametry nie zostały przekazane do w i h ...

open class Base(val w: Int, val h: Int)

class Derived(x: Int) : Base() {
    //                       ^ error
    init {
        // some complex derivation
        val c = Converter(x)
        // How do I provide c.a and c.b?
    }
}

class Converter(x: Int) {
    val a: Int
    val b: Int

    init {
        a = x + 2
        b = x - 2
    }
}

Jakie jest ogólne rozwiązanie tego problemu? (Oczywiście, nie robię nic tak prostego, jak pokazano tutaj. Uproszczone, aby przedstawić problem.)

0
Joe Lapp 1 maj 2019, 00:56

2 odpowiedzi

Najlepsza odpowiedź

Proponowałbym użycie prostszego, prywatnego konstruktora i dodawania metody fabrycznej , aby wykonać konwersję, np.:

class Derived private constructor(val w: Int, val h: Int) : Base(w, h) {
    companion object {
        operator fun invoke(x: Int): Derived {
            // Some complex derivation…
            val c = Converter(x)
            return Derived(c.a, c.b)
        }
    }
}

Można to wywołać dokładnie tak samo jak konstruktor, np. val d = Derived(1), ale ma kilka zalet:

  • Może to zrobić dużo przetwarzania przed wywołaniem konstruktora.

  • W stosownych przypadkach może zwrócić buforowane wartości zamiast nowych instancji.

  • Może zwrócić podklasę. (Więc Derived może być abstrakcyjną klasą - albo w ogóle nie będziesz tego potrzebować.) Dokładna klasa może się różnić między połączeniami, a nawet może być anonimowym typem.

  • Może mieć nazwę. Jest to szczególnie ważne, jeśli potrzebujesz wielu metod przyjmujących te same parametry. (Np. Obiekt punktowy, który może być skonstruowany z prostokątnych lub polarnych współrzędnych.) Jednak metoda fabryczna nie wymaga określonej nazwy; Jeśli zaimplementujesz metodę invoke() w obiekcie towarzyszącym (jak powyżej), możesz nazwać go dokładnie w taki sam sposób jak konstruktor.

  • Ułatwia zmianę realizacji klasy bez wpływu na jego publiczny interfejs.

Jest jednak jedna wada:

  • Nie można go używać przez konstruktorów podklasy.

W przeciwieństwie do odpowiedzi opartej na nieruchomości, nie wymaga to żadnych zmian w pozostałej części klas i nie przechowuje obiektu Convertor; Jest czysto inny sposób dzwonienia do konstruktora.

(W niektórych przypadkach możesz nie trzeba robić pierwotnego konstruktora prywatnego; może zapewnić alternatywę dla metody fabryki, o ile podpisy nie pasują).

1
gidds 2 maj 2019, 00:00

Komentarz JB Nizet Powyżej Ustaw mnie na właściwym Track: Nie możesz tego zrobić w ten sposób w Javie. Wiązałem się w węzłach, próbując robić rzeczy w Kotlin sposób. W tym przypadku próbuję użyć właściwości wszędzie, których użyłem metod akcesoriów w Javie.

Jednym rozwiązaniem jest stwarzanie właściwości podstawowych:

abstract class Base {
    abstract val w: Int
    abstract val h: Int

    // base implementation
}

class Derived(x: Int) : Base() {
    private val c = Converter(x)

    override val w: Int
        get() = c.a

    override val h: Int
        get() = c.b
}

class Converter(x: Int) {
    // implementation not relevant here
    val a = x + 2
    val b = x - 2
}
0
Joe Lapp 3 maj 2019, 04:26