Czy istnieje metoda, inna niż za pomocą statycznych metod fabrycznych lub wzorców konstruktora, do obsługi domyślnych parametrów konstruktora typu tablica?

tu toczy się wiele owocnych dyskusji na temat tego, jak to zrobić za pomocą wzorców konstruktora i statyczne metody fabryczne i istnieje wiele innych przykładów obsługi różnych konstruktorów o różnej liczbie parametrów, ale wszystkie te wzorce wydają się zawierać tylko parametry prostego typu (np. int, double itp.), których nie mam problem z dostaniem się do pracy. Próbuję mieć domyślny parametr tablicy typu i nie mogę tego osiągnąć za pomocą standardowej konfiguracji konstruktora.

Wypróbował kilka podejść do tego i wszystkie skutkują jakimś błędem, więc szukam alternatywy, wciąż wykorzystując wzorzec konstruktora. Pierwsze podejście:

public class MyClass{

    double[] mWeights;

    public MyClass(){
        double[] weights = {1, 1, 1}
        this(weights);
    }

    public MyClass(double[] weights){
        this.mWeights = weights
    }
}

Ale to powoduje błąd:

Wywołanie „this ()” musi być pierwszą instrukcją w treści konstruktora

Alternatywnie próbowałem:

public class MyClass{

    double[] mWeights = new double[] {1, 1, 1};  

    public ActionDistribution(){
        this(mWeights); 
    }  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

Ale to powoduje błąd:

Nie można odwołać się do „MyClass.mWeights” przed wywołaniem konstruktora typu supertype.

Na koniec próbowałem:

public class MyClass{

    double[] Mweights;  

    public ActionDistribution(){
        this({1, 1, 1}); 
    }  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

Ale to powoduje błąd:

Inicjator tablicy nie jest tutaj dozwolony

Jakieś pomysły, jak mogę skonfigurować konstruktor do obsługi domyślnej tablicy bez użycia statycznych metod fabrycznych lub wzorców konstruktora? Dodatkowe punkty, jeśli masz rozwiązanie, które działa dla dowolnego typu ogólnego (a nie tylko tablic), a jeszcze więcej punktów za wyjaśnienie, dlaczego jest to możliwe w przypadku prostszych typów danych, a nie tablic.

Edycja: Jeśli nie jest to jasne, w działającej formie powyższego kodu mam nadzieję, że później zadzwonię

MyClass myClass = new MyClass();

I daje w wyniku obiekt myClass z polem myClass.mWeights o wartości {1, 1, 1}

4
topher217 20 listopad 2019, 13:02

3 odpowiedzi

Najprawdopodobniej chcesz użyć składni new double[] { ... }:

class MyClass{

    public MyClass(){
        this(new double[] { 1, 1, 1});
    }

    public MyClass(double[] weights){
        this.mWeights = weights;
    }
}

Składnia tworzenia tablicy jest wyjaśniona w §JLS 15.10.1. Wyrażenia tworzenia tablicy. Zgodnie z § 10.6. Inicjatory tablicy:

Inicjator tablicy można określić w deklaracji pola (§8.3, §9.3) lub deklaracji zmiennej lokalnej (§14.4) lub jako część wyrażenia tworzenia tablicy (§15.10.1), aby utworzyć tablicę i podać pewne wartości początkowe .

W twoim przypadku nie przypisujesz pola ani zmiennej lokalnej, więc nie możesz użyć skrótu inicjatora tablicy. Parametry metody (i konstruktora) wymagają pełnej składni tworzenia tablicy.

3
Karol Dowbecki 20 listopad 2019, 13:27
Tak! Dziękuję Ci. Chcesz skomentować, dlaczego możesz przekazywać prostsze typy bez tej składni?
 – 
topher217
20 listopad 2019, 13:12
Rozumiem, więc może nie ma uogólnionej odpowiedzi na pytanie, jak ustawić wartość domyślną do użycia w konstruktorze dowolnego typu danych, ponieważ wydaje się, że zależy od typu danych. Podsumowując, musisz upewnić się, że obiekt typu T jest inicjowany w pustym konstruktorze zgodnie z jego regułami inicjalizacji typu danych, a ta właściwa składnia inicjalizacji musi być całkowicie zawarta w wywołaniu this(). Ma sens, ale może to ma więcej sensu z punktu widzenia czytelności? Zwłaszcza jeśli zamierzasz mieć więcej niż jeden parametr.
 – 
topher217
20 listopad 2019, 13:36

Możesz także użyć ... składni:

public class MyClass {

    double[] mWeights;

    public MyClass(double... mWeights) {
        mWeights = mWeights;
    }
    public MyClass(){
        this(1, 2, 3);
    }

}
2
Boris Zhguchev 20 listopad 2019, 13:11
To również wydaje się działać, ale nie znam tej składni. Czy możesz wskazać mi dokumentację, abym mógł na ten temat przeczytać? Chciałbym porównać zalety / wady korzystania z tej tej odpowiedzi (znalezienie ciągu ... w google nie jest wydaje się to takie proste :P)
 – 
topher217
20 listopad 2019, 13:15
1
To cukier składni dla tablic... Właściwie jest to ta sama konstrukcja co ta... Ty może rzucić okiem na varargs
 – 
Boris Zhguchev
20 listopad 2019, 13:19
Dzięki za linki. Przeczytałem link varargs i zauważyłem: > Varargs mogą być używane tylko w końcowej pozycji argumentu Jak więc poradziłbyś sobie z przypadkiem wielu parametrów typu tablicy przy tej składni? np. public MyClass(double... mWeights1, double... mWeights2) zwróci błąd związany z cytowaną dokumentacją varargs. public MyClass(double... mWeights1, mWeights2) a nie public MyClass(double... {mWeights1, mWeights2}) również działają. Jakieś linki do dokumentu uogólniającego składnię wielokropka?
 – 
topher217
20 listopad 2019, 13:44
W przypadku 'MyClass(double... mWeights1, double... mWeights2)' nie zadziała i zgłosi wyjątek kompilacji. Odwrotne pytanie - jaki przypadek jest potrzebny?
 – 
Boris Zhguchev
20 listopad 2019, 15:02
Pytałem, czy znasz jakieś miejsce, w którym wyszczególniono ogólną składnię, jak używać wielokropków varargs, ponieważ dokumentacja Oracle nie była dla mnie zbyt szczegółowa. Chociaż mój przykładowy kod jest napisany dla jednego parametru, mój rzeczywisty przypadek użycia obejmuje wiele parametrów, więc miałem nadzieję, że zrozumiem składnię tego nieco lepiej, tak abym mógł wypróbować go z wieloma parametrami. Z przykładów tutaj wygląda na to, że jest to przydatne w przypadkach, w których chcesz przejść przez pętlę pojedyncza tablica o zmiennej długości.
 – 
topher217
20 listopad 2019, 15:42

Możesz po prostu użyć drugiego podejścia i zamiast tego pozostawić swój konstruktor pusty:

public class MyClass {

    double[] mWeights = new double[] {1, 1, 1};  

    public ActionDistribution(){}  

    public ActionDistribution(double[] weights){  
        this.mWeights = weights;
    }
}

Inicjalizacja klasy za pomocą domyślnego pustego konstruktora pozostawi wagi, jakie zostały zainicjowane w polu Class za pomocą {1, 1, 1}.

1
T A 20 listopad 2019, 13:15
1
Lubię to. Wydaje się czystszy niż inne rozwiązania.
 – 
topher217
20 listopad 2019, 13:21
1
Tworzy również nadmiarową tablicę mWeights, która zostanie odrzucona przez konstruktor double[]. Być może JVM jest wystarczająco sprytny, aby zoptymalizować ten przypadek użycia, ale wydaje się niechlujny.
 – 
Karol Dowbecki
20 listopad 2019, 13:24
Jak powiedziałeś, zostanie odrzucony, więc nie będzie żadnych problemów z pamięcią. Wątpię, czy będzie to miało jakikolwiek wpływ na wydajność.
 – 
T A
20 listopad 2019, 13:35
@KarolDowbecki, przynajmniej w moim przypadku użycia, nie przejmuję się tym zbytnio, nawet jeśli byłyby pewne wady pamięci, ponieważ ta odpowiedź sprawiłaby, że kod byłby znacznie bardziej czytelny dla wielu parametrów. Chociaż obie odpowiedzi potencjalnie mają swoje wady i zalety.
 – 
topher217
20 listopad 2019, 13:39