Arasındaki fark nedir ? jeneriklerinde Teknoloji ve Nesne?


137

Eclipse, Java jeneriklerini doğru şekilde kullanmak için bazı kodları temizlememe yardımcı olmak için kullanıyorum. Çoğu zaman çıkarım türlerinin mükemmel bir işini yapar, ancak çıkarım türünün mümkün olduğunca genel olması gereken bazı durumlar vardır: Nesne. Ancak Eclipse bana bir tür Nesne ve bir tür '?' Arasında seçim yapma seçeneği sunuyor gibi görünüyor.

Peki arasındaki fark nedir:

HashMap<String, ?> hash1;

ve

HashMap<String, Object> hash2;

4
Joker karakterlerle ilgili resmi eğiticiye bakın . Bunu iyi açıklar ve neden sadece Object'i kullanmak için gerekli olduğuna dair bir örnek verir.
Ben S

Yanıtlar:


148

Bir HashMap<String, String>eşleşme örneği, Map<String, ?>ancak eşleşmiyor Map<String, Object>. Haritaları Strings'den herhangi bir şeye kabul eden bir yöntem yazmak istediğinizi varsayalım:

public void foobar(Map<String, Object> ms) {
    ...
}

a sağlayamazsınız HashMap<String, String>. Yazarsan

public void foobar(Map<String, ?> ms) {
    ...
}

işe yarıyor!

Java'nın jeneriklerinde bazen yanlış anlaşılan bir şey List<String>, bir alt türü değildir List<Object>. (Ama String[]aslında bir alt türü, Object[]jenerik ve dizilerin iyi karışmamasının nedenlerinden biri. (Java'daki diziler kovaryant, jenerik değil, değişmez )).

Örnek: Eğer kabul eden bir yöntem yazmak isterseniz Listlerini InputStreams ve alt tiplerini InputStream, yazmak istiyorum

public void foobar(List<? extends InputStream> ms) {
    ...
}

Bu arada: Joshua Bloch'un Etkili Java'sı , Java'daki o kadar basit olmayan şeyleri anlamak istediğinizde mükemmel bir kaynaktır. (Yukarıdaki sorunuz kitapta da ele alınmıştır.)


1
tüm denetleyici işlevlerim için ResponseEntity <?> denetleyicisini kullanmanın doğru yolu nedir?
Irakli

kusursuz cevap Johannes!
gaurav

36

Bu sorunu düşünmenin başka bir yolu da

HashMap<String, ?> hash1;

eşittir

HashMap<String, ? extends Object> hash1;

Bu bilgiyi Java Generics and Collections'ın (2.4) bölümündeki "Al ve Koy Prensibi" ile birleştirin :

Al ve Koyma İlkesi: yalnızca bir yapıdan değerler aldığınızda, genişletilmiş bir joker karakter kullanın, yalnızca bir yapıya değerler koyduğunuzda süper joker karakter kullanın ve hem alıp koyduğunuzda bir joker karakter kullanmayın.

ve umarım çılgın kart daha anlamlı gelebilir.


1
Eğer "?" sizi şaşırtır, "? Object genişletir" muhtemelen daha fazla karıştırır. Olabilir.
Michael Myers

Kişinin bu zor konu hakkında akıl yürütmesine izin vermek için "düşünme araçları" sağlamaya çalışmak. Joker karakterleri genişletme hakkında ek bilgi sağladı.
Julien Chastang

2
Ek bilgi için teşekkürler. Hala sindiriyorum. :)
skiphoppy

HashMap<String, ? extends Object> yani sadece nullhashmap'e eklenmesini önler ?
mallaudin

12

Bunun Collection<Object>yalnızca tür nesneler içeren genel bir koleksiyon olduğunu Object, ancak Collection<?>tüm koleksiyon türlerinin süper bir türü olduğunu hatırlarsanız kolay olur .


1
Bunun tam olarak kolay olmadığı söylenecek bir nokta var ;-), ama doğru.
Sean Reilly

6

Kovaryansın üzerindeki cevaplar çoğu vakayı kapsar, ancak bir şeyi kaçırır:

"?" sınıf hiyerarşisinde "Object" i içerir. Dize bir Nesne türü ve Nesne bir? Her şey Object ile eşleşmez, ama her şey eşleşir?

int test1(List<?> l) {
  return l.size();
}

int test2(List<Object> l) {
  return l.size();
}

List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1);  // compiles because any list will work
test1(l2);  // compiles because any list will work
test2(l1);  // fails because a ? might not be an Object
test2(l2);  // compiled because Object matches Object

4

Güvenli bir şey koyamazsın Map<String, ?> , çünkü değerlerin ne tür olması gerektiğini bilmiyorsunuz.

Herhangi bir nesneyi a içine koyabilirsiniz Map<String, Object>, çünkü değerin bir olduğu bilinmektedir Object.


Msgstr "Harita <String,?> İçine güvenli bir şey koyamazsınız." Yanlış. Yapabilirsin, amacı bu.
Ben S

3
Ben yanlış, <?> Türündeki bir koleksiyona koyabileceğiniz tek değer null, oysa <Object> türündeki bir koleksiyona herhangi bir şey koyabilirsiniz.
sk.

2
Cevabımda verdiğim bağlantıdan: "c türünün ne anlama geldiğini bilmediğimizden, ona nesne ekleyemeyiz.". Yanlış bilgi verdiğim için özür dilerim.
Ben S

1
en büyük sorun HashMap <String,?> içine nasıl bir şey ekleyemeyeceğinizi anlamıyorum. Yoksa ilkel için mi?
avalon

1
@avalon Başka yerlerde, bu haritaya bağlı bir referans var. Örneğin, bir olabilir Map<String,Integer>. Yalnızca Integernesneler haritaya değerler olarak kaydedilmelidir. Eğer değere (öyle türünü bilmiyorum çünkü Ama ?), bunu aramak için güvenli olmadığını bilmiyorum put(key, "x"), put(key, 0)veya başka bir şey.
erickson

2

hash1Bir HashMap<String, ?>değişken olarak bildirmek , değişkenin bir anahtarı olan ve herhangi bir değer türüne sahip hash1olabileceğini belirtir .HashMapString

HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();

Yukarıdakilerin hepsi geçerlidir, çünkü değişken map bu karma haritalardan herhangi birini saklayabilir. Bu değişken Değer türünün ne olduğu, sahip olduğu hasminin umrunda değil.

Bir joker olması gelmez değil , ancak, haritanıza nesne her türlü etsinler. Aslında, yukarıdaki karma harita ile, mapdeğişkeni kullanarak içine hiçbir şey koyamazsınız :

map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");

Yukarıdaki yöntem çağrılarının tümü derleme zamanı hatasına neden olur, çünkü Java içindeki HashMap'in Değer türünün ne olduğunu bilmez map.

Hala karma haritadan bir değer elde edebilirsiniz. "Değerin türünü bilmiyor olsanız da" (değişkeninizin içinde ne tür bir karma harita olduğunu bilmediğiniz için), her şeyin bir alt sınıf olduğunu Objectve dolayısıyla haritadan ne çıkardığınızı söyleyebilirsiniz. Object türünde olacaktır:

HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.

myMap.put("ABC", 10);

HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).

System.out.println(output);

Yukarıdaki kod bloğu 10'u konsola yazdıracaktır.


Bu nedenle, bitirmek için, HashMaptürlerinin ne olduğunu umursamadığınızda (yani önemli değil) joker karakterli bir kullanın HashMap, örneğin:

public static void printHashMapSize(Map<?, ?> anyMap) {
    // This code doesn't care what type of HashMap is inside anyMap.
    System.out.println(anyMap.size());
}

Aksi takdirde, ihtiyacınız olan türleri belirtin:

public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
    for (int i = 'A'; i <= 'Z'; i++)
        System.out.println(anyCharacterMap.get((char) i));
}

Yukarıdaki yöntemde, Harita'nın anahtarının bir olduğunu bilmemiz gerekir Character, aksi takdirde değer almak için hangi türün kullanılacağını bilemeyiz. toString()Bununla birlikte, tüm nesnelerin bir yöntemi vardır, böylece harita değerleri için herhangi bir nesne türüne sahip olabilir. Değerleri yine de yazdırabiliriz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.