2009-05-16 20 views
49

Klasa java.util.Properties ma reprezentować mapę, na której klucze i wartości są obydwoma łańcuchami. Dzieje się tak, ponieważ obiekty Properties są używane do odczytu plików .properties, które są plikami tekstowymi.Dlaczego java.util.Properties implementuje Mapę <Obiekt, Obiekt>, a nie Mapę <Ciąg, Ciąg>

Dlaczego w Javie 5 dokonali modernizacji tej klasy, aby wdrożyć Map<Object,Object>, a nie Map<String,String>?

W javadoc stany:

Ponieważ właściwości dziedziczy z Hashtable, nakładane i metody putAll można zastosować do obiektu Właściwości. Ich użycie jest zdecydowanie odradzane, ponieważ pozwalają wywołującym wstawiać wpisy, których kluczami lub wartościami nie są łańcuchy. Zamiast tego należy użyć metody setProperty. Jeśli metoda przechowywania lub składowania jest wywoływana w obiekcie "zagrożonym" właściwości, który zawiera klucz lub wartość inną niż String, wywołanie zakończy się niepowodzeniem.

Ponieważ klucze i wartości mają być zarówno ciągi, to dlaczego nie wymusić tego statycznie przy użyciu odpowiedniego rodzaju rodzajowego?

Chyba dokonywania Properties wdrożyć Map<String,String> nie byłby w pełni wstecznie kompatybilne z kodu napisanego dla pre-Java 5. Jeśli masz starszą kod, który przykleja niebędące struny do właściwości obiektu, a następnie, że kod nie będzie już skompilować z Java 5. Ale ... czy to nie jest dobre? Czy cały czas generics nie jest w stanie wychwycić takich błędów typu podczas kompilacji?

Odpowiedz

50

Ponieważ zrobili to w pośpiechu we wczesnych dniach Java i nie zdawali sobie sprawy, jakie konsekwencje będą miały cztery wersje później.

Generics miały być częścią projektu Javy od samego początku, ale funkcja została odrzucona jako zbyt skomplikowana i, w tym czasie, niepotrzebna. W rezultacie wiele kodu w standardowych bibliotekach jest zapisanych przy założeniu nietypowych kolekcji.Zajęło prototypowy język "Pizza" od Martina Odersky'ego, aby pokazać, jak można je wykonać dość dobrze, zachowując niemal idealną kompatybilność wsteczną, zarówno z kodem Java, jak iz bajtem kodu. Prototyp doprowadził do wersji Java 5, w której klasy kolekcji zostały doposażone w generyczne w sposób, który pozwalał na kontynuację pracy starego kodu.

Niestety, jeśli były one wstecznie uczynić Properties Dziedzicz Map<String, String>, wówczas po uprzednio ważny kod będzie przestać działać:

Map<Object, Object> x = new Properties() 
x.put("flag", true) 

Dlaczego ktoś miałby to robić poza mną, ale zaangażowanie Sun do wstecznej kompatybilności w Java poszła poza heroizm w bezsens.

Co jest teraz cenione przez większość wykształconych obserwatorów, to, że Properties nigdy nie powinien w ogóle odziedziczyć po Map. Zamiast tego powinien owinąć się wokół Map, ujawniając tylko te cechy Map, które mają sens.

Od czasu wynalezienia Java na nowo, Martin Odersky opracował nowy język Scala, który jest czystszy, dziedziczy mniej błędów i tworzy nowe obszary w wielu dziedzinach. Jeśli znajdziesz irytujące usterki Javy, spójrz na to.

+1

Właściwie Map x = new Properties() działa w dowolny sposób. Jego ludzie wkładają rzeczy takie jak properties.put ("flaga", Boolean.TRUE); Widziałem, jak ludzie umieszczają wszystkie rodzaje danych w obiekcie Properties, ale nigdy nie są kluczem innym niż String. ;) –

+0

Uaktualniłem moją odpowiedź, aby było jaśniejsze, dzięki. –

+5

Zaczekaj chwilę! Mapa x = nowe Właściwości() nie jest legalna przed wersją Java 5, składnia została wprowadzona w języku Java 5. Tak więc instrukcja "poprzednio prawidłowy kod" jest niepoprawna. –

2

Powód: Liskov substitution principle i kompatybilność wsteczna. Properties rozszerza Hashtable i dlatego musi akceptować wszystkie wiadomości, które zaakceptuje Hashtable - a to oznacza akceptację put(Object, Object). I musi rozszerzyć zwykły Hashtable zamiast Hashtable<String, String>, ponieważ Generics zostały zaimplementowane w sposób zgodny w dół przez type erasure, więc gdy kompilator zrobi swoje, nie ma generycznych.

+3

Nie sądzę zasada substytucji Liskov ma nic wspólnego z nim. Java z powodzeniem pozwala na zawężenie typów ogólnych, gdy dziedziczysz klasę. Właśnie dlatego i istnieje składnia . –

27

Pierwotnie zamierzano, że Properties rzeczywiście rozszerzy się o Hashtable<String,String>. Niestety wdrożenie metod mostowych spowodowało problem. Properties zdefiniowane w ten sposób powoduje, że javac generuje metody syntetyczne. Properties powinien zdefiniować, powiedzmy, metodę get, która zwraca wartość String, ale musi zastąpić metodę, która zwraca Object. Dodana została więc metoda syntetycznego mostu.

Załóżmy, że masz klasę napisaną w złym, starym, 1,4 dniu. Zastąpiłeś niektóre metody w Properties. Ale to, czego nie zrobiliście, jest nadrzędne wobec nowych metod. To prowadzi do niezamierzonego zachowania. Aby uniknąć tych metod mostkowania, Properties rozszerza się o Hashtable<Object,Object>. Podobnie Iterable nie zwraca (tylko do odczytu) SimpleIterable, ponieważ to by dodało metody do implementacji Collection.

+2

Dobre wyjaśnienie. Też się nad tym zastanawiałem. –

13

one-liner (two-liner żadnych ostrzeżeń) do tworzenia mapy od nieruchomości:

@SuppressWarnings({ "unchecked", "rawtypes" }) 
Map<String, String> sysProps = new HashMap(System.getProperties()); 
+0

Nie odpowiada na pytanie. Ale odpowiada przede wszystkim na pytanie, które spowodowało to pytanie. Od razu do rzeczy. –

+1

Czy konieczne są tu "surowe typy"? (Przynajmniej w mojej IntelliJ IDEA "odznaczone" jest wystarczające do stłumienia wszelkich ostrzeżeń.) – Jonik

Powiązane problemy