Jeśli moje IDE jest tak inteligentne, dlaczego muszę przesyłać „clone ()”?

13

Mój typ IDE ( NetBeans ) sprawdza mój Collectionspodczas pisania kodu. Ale dlaczego muszę rzucić zwrócony przedmiot Object.clone()? Co jest w porządku Bez szkody, bez faulu. Ale nadal nie rozumiem.

Czy sprawdzanie typu bez rzutowania zwrócony obiekt Object.clone()jest niemożliwe? Rodzajowych ramy sprawia, że myślę IDE mógł sprawdzić typ referencji obiektu na prawym boku „ = znakiem” bez rzucania gdy piszę? Nie rozumiem

addendum
Mój przypadek użycia polegał na tym, że miałem prywatne Calendarpole, data publikacji . Miałem zamiar napisać:

Calendar getPubdate() {
    return pubdate;
}

ale istnieje ryzyko, że wywołujący może zmodyfikować moją datę publikacji , więc zwróciłem kopię:

Calendar getPubdate() {
    return (Calendar) pubdate.clone();
}

Potem zastanawiałem się, dlaczego muszę rzucić pubdate.clone(). Podpis metody ma właśnie tam typ. NetBeans powinien być w stanie to rozgryźć. A NetBeans zdawał się robić coś podobnego w odniesieniu do Collections.

konishiki
źródło
5
Pomocny byłby krótki (!) Przykład kodu.
Doc Brown
78
IDE jest niezależne od samego języka, więc sposób, w jaki inteligentne Netbeans nie ma wpływu na działanie języka Java.
JacquesB
7
Uwaga: ze względu na kowariancję typu zwrotu zaleca się raczej powrót MyObjectz clone()niż Object- to usuwa cały ten problem. Ponadto zaleca się, aby nigdy nie używać clone()(Skuteczny element Java nr 11).
Boris the Spider
Czy nacisk kładziony jest na twoje pytanie , czy na „cast” clone () „” ? Ponieważ pytanie wynikające z pierwszego może być o wiele lepszym pytaniem niż drugie.
user541686,
Kiedy czytam tytuł pytania, jedyne, o czym myślę, to this.clone()obiekt programisty, szczególnie w noc Środy po wydaniu Tue. Przepraszam, ale muszę napisać ten komentarz. Dlaczego inteligentne IDE nie naprawia dla nas wszystkich błędów LOL
Mai

Odpowiedzi:

53

dlaczego muszę rzucić zwrócony obiekt Object.clone()?

Ponieważ powraca Object.

Rodzajowych ramy sprawia, że myślę IDE mógł sprawdzić typ referencji obiektu na prawym boku „ = znakiem” bez rzucania gdy piszę? Nie rozumiem

Object.clone nie jest ogólny.

Gdyby istniały leki generyczne, kiedy clonezostały zaprojektowane, prawdopodobnie wyglądałoby to tak (używając polimorfizmu F-Bounded):

interface Cloneable<T extends Cloneable<T>> {
  T clone();
}

Gdyby Java miała funkcję MyType, mogłaby wyglądać następująco:

interface Cloneable {
  this clone();
}

Ale Generics nie istniał, kiedy Object.clonezostał zaprojektowany, a Java nie ma MyTypes, więc Object.clonemusimy pracować z niebezpieczną wersją typu .

Jörg W Mittag
źródło
Właśnie zdałem sobie sprawę, że funkcja IDE, która mnie pomyliła, to kompilacja w tle? Mam na myśli to, co umożliwia uzupełnianie kodu i automatyczne sprawdzanie typów za pomocą generyków? Osobiście wolałbym opóźniać sprawdzanie typu aż do jawnej kompilacji. Ale uwielbiam uzupełniać kody.
konishiki,
1
@konishiki z Java 8 (i do pewnego stopnia Java 7) rodzaj wnioskowania , w celu zapewnienia wszelkiej pomocy, potrzeby IDE typów wywnioskować. Wymaga to częściowej kompilacji. Przed wnioskowaniem typu kompilacja nie jest wymagana do autouzupełniania.
Boris the Spider
@ BoristheSpider Bardzo dziękuję za informację zwrotną! Zdecydowanie potrzebuję więcej testów / myślenia. bardzo mile widziane.
konishiki,
Dlaczego miałbyś zdać original?
Clashsoft,
@Clashsoft: Głupi cienko, dostosowując oczywistą wersję programowania funkcjonalnego. Dzięki.
Jörg W Mittag
26

Nie jest to cecha żadnego IDE, ale definicji języka.

IDE pomaga efektywniej wykorzystywać język programowania, nie zmienia semantyki tego języka. Oznacza to, że pomocnik edytora może automatycznie wstawić rzutowanie, gdy jest oczywiste, który chcesz, ale nie może po prostu złamać reguł języka i udawać, że kod, który się nie kompiluje, jest poprawny.

Edycja Prawdą jest, że IDE może spakować swój własny kompilator, a wielu z nich właśnie to robi, np. W celu lepszego raportowania błędów z większą ilością informacji wewnętrznych w częściowym drzewie analizy. Byłoby jednak bardzo złym pomysłem, aby pozwolić temu wewnętrznemu kompilatorowi na implementację semantyki języka innej niż oficjalny SDK, ponieważ oznaczałoby to, że kod działający w fazie programowania może w tajemniczy sposób przestać działać po zainstalowaniu w środowisku produkcyjnym, powodując problemy, które z definicji są niespójne. debugowalne!

Kilian Foth
źródło
Oto co się stało: jestem prawie pewien, że uzupełnianie kodu jest funkcją IDE (wykonaną przez odbicie). A automatyczne sprawdzanie typu, o którym mówiłem, odbywa się w IDE (poprzez kompilację w tle). Mam nadzieję, że o to chodzi.
konishiki,
1
IDE może bardzo łatwo złamać wszystkie reguły specyfikacji języka i po prostu skompilować kod mimo to. Wiem, że zarówno NetBeans, jak i Eclipse używają na przykład własnych kompilatorów. W takich przypadkach IDE i kompilator są w rzeczywistości tym samym. To nie jest dobry pomysł, ale kompilatory C z pewnością robią to cały czas.
MichaelS
@MichaelS Specyfikacja języka C została zaprojektowana tak, aby umożliwić znaczne różnice między implementacjami, dzięki czemu różni dostawcy mogą wybierać między różnymi możliwymi podejściami i dodawać dodatkowe funkcje. Stwarza to różne obszary niejednoznaczności w specyfikacji, które są niezbędne, aby umożliwić te zmiany. Nazwa „C” nie jest znakiem towarowym, więc nikt nie kontroluje używania tego terminu. Specyfikacja Java ma na celu wyeliminowanie jak największej dwuznaczności, a Java jest znakiem towarowym, a właściciel znaku towarowego wymusza, aby można go było używać tylko w implementacjach zgodnych z wymaganiami.
Jules
Nowoczesne IDE i tak potrzebują kompilatora. Wskazówki dotyczące uzupełniania kodu wymagają, aby kod przed kursorem został przeanalizowany. I z pewnością w przypadku generycznych (lub szablonów C ++), które wymagają pełnego kompilatora, a nie tylko parsera.
MSalters
3

Wynika to z podpisu typu metody Object.clone. Podpis typu stwierdza, że ​​metoda zwróci obiekt typu Object.

protected Object clone() throws CloneNotSupportedException

Kolekcje wykorzystają tak zwane typy ogólne, aby automatycznie zastąpić typ rzutowania.

Więc jeśli masz ten kod:

List<Integer> ints = Arrays.asList(1,2,3);
int x = ints.get(0);`

kompilator doda dla Ciebie obsady, więc kod będzie w rzeczywistości:

List ints = Arrays.asList(1,2,3);
int x = (Integer)ints.get(0);
Miro
źródło
0

Dla kompletności, od Java 5, dozwolone są typy kowariancji zwracanych . Możesz więc napisać:

public MyObject implements Cloneable {
  @Override
  public MyObject clone() {
    try {
      return (MyObject)super.clone();
    } catch (CloneNotSupportedException e) {
      throw new AssertionError();
    }
  }
}

Dzięki temu następujący kod jest legalny:

MyObject original = new MyObject();
MyObject clone = original.clone();
Olivier Grégoire
źródło