Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何将超类型列表强制转换为子类型列表?_Java_List_Generics_Collections_Casting - Fatal编程技术网

Java 如何将超类型列表强制转换为子类型列表?

Java 如何将超类型列表强制转换为子类型列表?,java,list,generics,collections,casting,Java,List,Generics,Collections,Casting,例如,假设您有两个类: public class TestA {} public class TestB extends TestA{} 我有一个方法返回一个列表,我想将该列表中的所有对象强制转换为TestB,这样我就得到了一个列表,你真的不能*: 例子取自 假设有两种类型A和B,使得B扩展了A。 那么以下代码是正确的: B b = new B(); A a = b; 前面的代码是有效的,因为B是a的子类。 现在,如何处理列表和列表 原来List不是List的子类,因此我们不

例如,假设您有两个类:

public class TestA {}
public class TestB extends TestA{}
我有一个方法返回一个
列表
,我想将该列表中的所有对象强制转换为
TestB
,这样我就得到了一个
列表
,你真的不能*:

例子取自

假设有两种类型
A
B
,使得
B扩展了A
。 那么以下代码是正确的:

    B b = new B();
    A a = b;
前面的代码是有效的,因为
B
a
的子类。 现在,如何处理
列表
列表

原来
List
不是
List
的子类,因此我们不能写

    List<B> b = new ArrayList<>();
    List<A> a = b; // error, List<B> is not of type List<A>
listb=newarraylist();
列表a=b;//错误,列表不是列表类型
此外,我们甚至不能写

    List<B> b = new ArrayList<>();
    List<A> a = (List<A>)b; // error, List<B> is not of type List<A>
listb=newarraylist();
列表a=(列表)b;//错误,列表不是列表类型
*:为了使强制转换成为可能,我们需要为
List
List
List
提供一个共同的父级。以下有效:

    List<B> b = new ArrayList<>();
    List<?> t = (List<B>)b;
    List<A> a = (List<A>)t;
listb=newarraylist();
列表t=(列表)b;
列表a=(列表)t;

然而,你会得到一个警告。你可以通过在你的方法中添加
@SuppressWarnings(“unchecked”)
来抑制它。

我认为你在错误的方向上施法。。。如果该方法返回
TestA
对象的列表,那么将它们强制转换到
TestB
是不安全的


基本上,您要求编译器允许您对不支持它们的类型
TestA
执行
TestB
操作。

由于类型擦除,这是可能的。你会发现

List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true
List x=new ArrayList();
列表y=新的ArrayList();
x、 getClass().equals(y.getClass());//真的

在内部,两个列表的类型均为
List
。因此,您不能将一个对象强制转换为另一个对象-没有任何可强制转换的对象。

如果您有类
TestA
的对象,则无法将其强制转换为
TestB
。每个
TestB
都是
TestA
,但不是相反

在以下代码中:

TestA a = new TestA();
TestB b = (TestB) a;
第二行将抛出一个
ClassCastException

如果对象本身是
TestB
,则只能强制转换
TestA
引用。例如:

TestA a = new TestB();
TestB b = (TestB) a;

因此,您可能并不总是将
TestA
列表强制转换为
TestB

您不能像Steve Kuo提到的那样将
list
强制转换为
list
,但您可以将
list
的内容转储到
list
中。请尝试以下操作:

List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);
列表结果=新列表();
列表数据=新列表();
结果:addAll(数据);

我还没有尝试过这段代码,所以可能会有错误,但我的想法是它应该遍历数据对象,将元素(TestB对象)添加到列表中。我希望这对你有用。

简单地向
列表投球几乎可以;但它不起作用,因为不能将一个参数的泛型类型强制转换为另一个参数。但是,您可以强制转换中间通配符类型,这是允许的(因为您可以在通配符类型之间进行强制转换,只需发出未选中的警告):

List变量=(List)(List)collectionOfListA;

当您强制转换对象引用时,您只是强制转换引用的类型,而不是对象的类型。强制转换不会更改对象的实际类型

Java没有用于转换对象类型的隐式规则。(与基本体不同)

相反,您需要提供如何将一种类型转换为另一种类型并手动调用它

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));
}
公共类TestA{}
公共类TestB扩展了TestA{
TestB(TestA-TestA){
//从TestA构建TestB
}
}
列表结果=。。。。
列表数据=新列表();
用于(测试测试:结果){
添加(新测试B(测试A));
}

这比直接支持的语言更详细,但它可以工作,您不需要经常这样做。

转换泛型是不可能的,但如果您以另一种方式定义列表,则可以在其中存储TestB:

List<? extends TestA> myList = new ArrayList<TestA>();

List我知道的唯一方法是复制:

List<TestB> list = new ArrayList<TestB> (
    Arrays.asList (
        testAList.toArray(new TestB[0])
    )
);
List List=newarraylist(
Arrays.asList(
TestList.toArray(新TestB[0])
)
);

最好的安全方法是实现
抽象列表
并在实现中强制转换项。我创建了
ListUtil
helper类:

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();
            }
        };
    }
}

使用Java8,您实际上可以

List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());
List变量=collectionOfListA
.stream()
.map(e->(TestB)e)
.collect(Collectors.toList());
由于这是一个被广泛引用的问题,而当前的答案主要解释了它不起作用的原因(或者提出了我永远都不想在生产代码中看到的骇客、危险的解决方案),因此我认为添加另一个答案是合适的,显示了陷阱和可能的解决方案


在其他答案中已经指出了这通常不起作用的原因:转换是否实际有效取决于原始列表中包含的对象的类型。如果列表中的对象的类型不是
TestB
,而是
TestA
的不同子类,则转换无效


当然,强制转换可能是有效的。您有时会获得编译器无法使用的类型信息。在这些情况下,可以对列表进行强制转换,但通常不建议使用该列表:

一个人可以

  • 。。。投下全部名单或
  • 。。。强制转换列表中的所有元素
第一种方法(对应于当前接受的答案)的含义是微妙的。乍一看,它似乎工作正常。但是如果输入列表中有错误的类型,那么
ClassCastException
wil
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
}
List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());
/**
 * 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;
}
// 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]
class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;
List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist;  // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!
List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;
List<TestA> testAList = new ArrayList<>();
List<TestB> testBList = new ArrayList<>()
testAList.addAll(new ArrayList<>(testBList));
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);
}
interface ITest {
}

class TestA implements ITest {
}

class TestB extends TestA {
}

public class Main {

  public static void main(String[] args) {
    List<ITest> testAList = Arrays.asList(new TestA(), new TestA());

    Class<ITest> clazz = ITest.class;
    List<ITest> testBList = testAList.stream()
        .map(clazz::cast)
        .collect(Collectors.toList());

    System.out.println(testBList.size());
    System.out.println(testAList);
  }
}
class TestA {
}

class TestB extends TestA {
}

public class Main {

  public static void main(String[] args) {
    var a = new TestA();
    var b = new TestB();
    System.out.println(((TestA) b));

    List<TestB> testBList = Arrays.asList(new TestB(), new TestB());

    Class<TestA> clazz = TestA.class;
    List<TestA> testAList = testBList.stream()
        .map(clazz::cast)
        .collect(Collectors.toList());

    System.out.println(testAList.size());
    System.out.println(testAList);
  }
}
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);
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
    return (List) list;
}