Örneğin, iki sınıfınız olduğunu varsayalım:
public class TestA {}
public class TestB extends TestA{}
Bir döndüren bir yöntem var List<TestA>
ve ben TestB
böylece sonunda ile bu listedeki tüm nesneleri döküm istiyorum List<TestB>
.
Örneğin, iki sınıfınız olduğunu varsayalım:
public class TestA {}
public class TestB extends TestA{}
Bir döndüren bir yöntem var List<TestA>
ve ben TestB
böylece sonunda ile bu listedeki tüm nesneleri döküm istiyorum List<TestB>
.
Yanıtlar:
List<TestB>
Neredeyse işe döküm ; ancak çalışmaz, çünkü bir parametrenin genel türünü diğerine atayamazsınız. Bununla birlikte, bir ara joker karakter türünden yayın yapabilirsiniz ve buna izin verilir (joker karakter türlerine ve bu tür karakterlerden yalnızca işaretlenmemiş bir uyarı ile yayınlayabileceğiniz için):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
jeneriklerin dökümü mümkün değildir, ancak listeyi başka bir şekilde tanımlarsanız, TestB'yi depolamak mümkündür:
List<? extends TestA> myList = new ArrayList<TestA>();
Listedeki nesneleri kullanırken hala yapılacak tür denetiminiz vardır.
Gerçekten yapamazsınız *:
Bu Java eğitiminden örnek alınmıştır
İki türü vardır varsayalım A
ve B
öyle ki B extends A
. Sonra aşağıdaki kod doğrudur:
B b = new B();
A a = b;
Önceki kod geçerlidir, çünkü B
bir alt sınıfı A
. Şimdi, List<A>
ve ile ne olur List<B>
?
O çıkıyor List<B>
bir alt sınıfı değildir List<A>
bu nedenle olamaz yazma
List<B> b = new ArrayList<>();
List<A> a = b; // error, List<B> is not of type List<A>
Ayrıca, olamaz bile yazmak
List<B> b = new ArrayList<>();
List<A> a = (List<A>)b; // error, List<B> is not of type List<A>
*: Biz her ikisi için ortak bir ebeveyn gerek döküm mümkün kılmak için List<A>
ve List<B>
: List<?>
örneğin. Aşağıdaki olduğunu geçerlidir:
List<B> b = new ArrayList<>();
List<?> t = (List<B>)b;
List<A> a = (List<A>)t;
Ancak bir uyarı alacaksınız. @SuppressWarnings("unchecked")
Yönteminize ekleyerek bunu bastırabilirsiniz .
Java 8 ile,
List<TestB> variable = collectionOfListA
.stream()
.map(e -> (TestB) e)
.collect(Collectors.toList());
new List<TestB>
, loop ve cast ile?
Bu yaygın olarak başvurulan bir soru olduğundan ve mevcut cevaplar esas olarak neden işe yaramadığını (veya üretim kodunda hiç görmek istemediğim hileli, tehlikeli çözümler önerdiğini) açıkladığından, başka bir cevap eklemenin uygun olduğunu düşünüyorum. tuzaklar ve olası bir çözüm.
Bunun genel olarak çalışmadığının nedeni diğer cevaplarda zaten belirtilmiştir: Dönüştürmenin gerçekten geçerli olup olmadığı, orijinal listede bulunan nesnelerin türüne bağlıdır. Listede türü türü olmayan TestB
, ancak farklı bir alt sınıfı olan nesneler olduğunda TestA
, döküm geçerli değildir .
Tabii ki, atmalar geçerli olabilir . Bazen derleyici tarafından kullanılamayan türler hakkında bilgi sahibi olabilirsiniz. Bu durumlarda, listeleri yayınlamak mümkündür, ancak genel olarak önerilmez :
Ya biri ...
İlk yaklaşımın (şu anda kabul edilen cevaba karşılık gelen) yansımaları belirsizdir. İlk bakışta düzgün çalışıyor gibi görünebilir. Ancak giriş listesinde yanlış türler varsa ClassCastException
, belki kodda tamamen farklı bir yere a atılır ve bu hata ayıklamak ve yanlış öğenin listeye nerede kaydığını bulmak zor olabilir. En kötü sorun, birisinin geçersiz öğeleri sonradan ekleyebilmesidir. liste yayınlandıktan hata ayıklamayı daha da zor hale getirmesidir.
Bu sahte hata ayıklama sorunu yöntemler ailesi ClassCastExceptions
ile hafifletilebilir Collections#checkedCollection
.
Bir dönüştürme ilişkin daha tip-güvenli yolu List<Supertype>
bir etmek List<Subtype>
aslında etmektir filtre listesi ve belirli bir tür tek elemanlarını içeren yeni bir liste oluşturmak. Böyle bir yöntemin uygulanması için bazı serbestlik dereceleri vardır (örneğin, null
girişlerin tedavisi ile ilgili ), ancak olası bir uygulama şöyle görünebilir:
/**
* Filter the given list, and create a new list that only contains
* the elements that are (subtypes) of the class c
*
* @param listA The input list
* @param c The class to filter for
* @return The filtered list
*/
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
List<T> listB = new ArrayList<T>();
for (Object a : listA)
{
if (c.isInstance(a))
{
listB.add(c.cast(a));
}
}
return listB;
}
Bu yöntem, bu örnekte olduğu gibi, rasgele listeleri (yalnızca tür parametrelerine ilişkin belirli bir Alt Tür-Süpertip ilişkisiyle değil) filtrelemek için kullanılabilir:
// A list of type "List<Number>" that actually
// contains Integer, Double and Float values
List<Number> mixedNumbers =
new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));
// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);
System.out.println(integers); // Prints [12, 78]
Sen döküm olamaz List<TestB>
için List<TestA>
Steve Kuo bahseder AMA sen içeriğini dökümü gibi List<TestA>
içine List<TestB>
. Takip etmeyi dene:
List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);
Muhtemelen hatalar bu yüzden bu kodu denemedim ama fikir listeye öğeleri (TestB nesneleri) ekleyerek veri nesnesi üzerinden yinelemek gerekir olmasıdır. Umarım bu işe yarar.
En güvenli yol, uygulamada bir AbstractList
ve döküm öğeleri uygulamaktır. ListUtil
Yardımcı sınıf oluşturdum :
public class ListUtil
{
public static <TCastTo, TCastFrom extends TCastTo> List<TCastTo> convert(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
public static <TCastTo, TCastFrom> List<TCastTo> cast(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return (TCastTo)list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
}
Sen kullanabilirsiniz cast
listesi ve de körü körüne dökme nesnelere yöntemi convert
güvenli döküm için yöntemiyle. Misal:
void test(List<TestA> listA, List<TestB> listB)
{
List<TestB> castedB = ListUtil.cast(listA); // all items are blindly casted
List<TestB> convertedB = ListUtil.<TestB, TestA>convert(listA); // wrong cause TestA does not extend TestB
List<TestA> convertedA = ListUtil.<TestA, TestB>convert(listB); // OK all items are safely casted
}
Bilmemin tek yolu kopyalamaktır:
List<TestB> list = new ArrayList<TestB> (
Arrays.asList (
testAList.toArray(new TestB[0])
)
);
Bir nesne başvurusunu yayınladığınızda, yalnızca nesnenin türünü değil referans türünü kullanırsınız. döküm nesnenin gerçek türünü değiştirmez.
Java, Nesne türlerini dönüştürmek için örtük kurallara sahip değildir. (İlkellerden farklı olarak)
Bunun yerine, bir türün diğerine nasıl dönüştürüleceğini ve manuel olarak nasıl çağrılacağını sağlamanız gerekir.
public class TestA {}
public class TestB extends TestA{
TestB(TestA testA) {
// build a TestB from a TestA
}
}
List<TestA> result = ....
List<TestB> data = new List<TestB>();
for(TestA testA : result) {
data.add(new TestB(testA));
}
Bu, doğrudan destekli bir dilde olduğundan daha ayrıntılıdır, ancak işe yarar ve bunu çok sık yapmanız gerekmez.
sınıfın bir nesnesi TestA
varsa, onu yayınlayamazsınız TestB
. her TestB
şey bir TestA
, ama başka bir yol değil.
aşağıdaki kodda:
TestA a = new TestA();
TestB b = (TestB) a;
ikinci satır a ClassCastException
.
yalnızca TestA
nesnenin kendisi olduğunda bir referans oluşturabilirsiniz TestB
. Örneğin:
TestA a = new TestB();
TestB b = (TestB) a;
böylece, her zaman bir listesini kullanamazlar TestA
listesine TestB
.
Bu selectInstances
yöntemi Eclipse Collections'ta kullanabilirsiniz . Bu, yeni bir koleksiyon oluşturmayı içerecektir, ancak döküm kullanan kabul edilen çözüm kadar etkili olmayacaktır.
List<CharSequence> parent =
Arrays.asList("1","2","3", new StringBuffer("4"));
List<String> strings =
Lists.adapt(parent).selectInstancesOf(String.class);
Assert.assertEquals(Arrays.asList("1","2","3"), strings);
Ben sadece tip downcasts StringBuffer
göstermek selectInstances
değil, aynı zamanda koleksiyon karışık türleri içeriyorsa filtre edecek göstermek için örneğe dahil .
Not: Eclipse Collections için bir komisyoncuyum.
Bu, tip silme nedeniyle mümkündür. Onu bulacaksın
List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true
Dahili olarak her iki liste de türdedir List<Object>
. Bu nedenle birini diğerine dökemezsiniz - dökülecek hiçbir şey yoktur.
Integer j = 42; Integer i = (Integer) j;
iyi çalışıyor, her ikisi de tamsayı, ikisi de aynı sınıfa sahip, yani "dökülecek bir şey yok".
Sorun şu ki, yöntem bir TestB içeriyorsa TestA listesini döndürmez, bu yüzden doğru yazılırsa ne olur? Sonra bu oyuncu kadrosu:
class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;
umut edebileceğiniz kadar iyi çalışır (Eclipse sizi tam olarak ne yaptığınızı kontrol etmeyen bir oyuncu olarak uyarır, bu yüzden meh). Peki bunu probleminizi çözmek için kullanabilir misiniz? Aslında bunun için şunları yapabilirsiniz:
List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist; // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!
Tam olarak ne istedin? veya tam olarak söylemek gerekirse:
List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;
benim için gayet iyi derlenmiş gibi görünüyor.
class MyClass {
String field;
MyClass(String field) {
this.field = field;
}
}
@Test
public void testTypeCast() {
List<Object> objectList = Arrays.asList(new MyClass("1"), new MyClass("2"));
Class<MyClass> clazz = MyClass.class;
List<MyClass> myClassList = objectList.stream()
.map(clazz::cast)
.collect(Collectors.toList());
assertEquals(objectList.size(), myClassList.size());
assertEquals(objectList, myClassList);
}
Bu test gösterileri nasıl atmak List<Object>
için List<MyClass>
. Ancak objectList
, aynı türden örnekleri içermesi gerektiğine dikkat etmeniz gerekir MyClass
. Ve bu örnek List<T>
kullanıldığında dikkate alınabilir . Bu amaçla kurucuda alan alın Class<T> clazz
ve onun yerine kullanın MyClass.class
.
Bu işe yarar
List<TestA> testAList = new ArrayList<>();
List<TestB> testBList = new ArrayList<>()
testAList.addAll(new ArrayList<>(testBList));
Bir listeyi manuel olarak yayınlamanın, aşağıdaki gibi bir araç uygulayan bazı araç kutusu tarafından hala sağlanmadığı oldukça garip:
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
return (List) list;
}
Tabii ki, bu öğeleri tek tek kontrol etmeyecektir, ancak uygulamamızın yalnızca alt türü sağladığını iyi bildiğimizde, burada kaçınmak istediğimiz şey tam olarak budur.