为了账号安全,请及时绑定邮箱和手机立即绑定

多种类型的Java列表是否可能

多种类型的Java列表是否可能

30秒到达战场 2021-11-11 15:57:04
昨天我偶然发现了一些我无法解释的奇怪的 Java/Spring/IntelliJ 行为。这是一个使用 jdk1.8.0_152 制作的 Spring Boot 应用程序。我运行了这个简单的 SQL 来填充我的数据库:CREATE TABLE TEST_ORGANIZATION(    ID NUMBER(19) NOT NULL,    NAME VARCHAR(255) NOT NULL);INSERT INTO TEST_ORGANIZATION (ID, NAME) VALUES ('1', 'test1');INSERT INTO TEST_ORGANIZATION (ID, NAME) VALUES ('2', 'test2');这是我的实体类:@Data@NoArgsConstructor@Entitypublic class TestOrganization {    @Id    private Long id;    @NotNull    private String name;}还有我的 JPA 存储库:public interface TestOrganizationRepository extends JpaRepository<TestOrganization, Long> {    @Query("SELECT new map(id as key, name as value) FROM TestOrganization")    List<Map<String, String>> findAllAndMapById();}这就是事情变得混乱的地方。我编写了一个简单的单元测试来检查值,但结果在第二次断言时失败:@Testpublic void shouldGetDocumentByName() {    List<String> values = testOrganizationRepository.findAllAndMapById()            .stream()            .flatMap( m -> m.values().stream() )            .collect( Collectors.toList() );    assertThat( values ).isNotEmpty();    assertThat( values ).allMatch( n -> n instanceof String );}使用 IntelliJ 进行调试时,它显示如下值:这怎么可能?为什么我可以在字符串列表中拥有 Long 值?还: values.add(1L) // fails to compile values.get(0).getClass().name // returns java.lang.String values.get(1).getClass().name // returns java.lang.Long
查看完整描述

3 回答

?
繁星点点滴滴

TA贡献1803条经验 获得超3个赞

这是可能的,因为 Java 中的类型擦除。简而言之,这意味着在运行时 Java 不知道您的泛型类型,因此任何List对象都使用Object类型进行操作。泛型具有编译类型安全性。但是您可以更详细地了解类型擦除,例如这里。


您可以自己模仿的相同行为:


public static void main(String[] args) {

    List<String> list = new ArrayList<>();

    addToList(list);

    System.out.println(list);

}


private static void addToList(List list) {

    list.add(1L);

    list.add(42);

    list.add("String");

    list.add(new Runnable() {

        @Override

        public void run() {}

    });

}

只要您不尝试将列表条目作为字符串进行操作,此代码就可以正常工作。但是当你添加如下内容时:


for (String s : list) {

    System.out.println(s);

}

你会得到java.lang.ClassCastException。


因此,在编译时,您可以使用List<String>,但在运行时,Java 只知道List.


查看完整回答
反对 回复 2021-11-11
?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

是的,你可以,这是一个小例子


public static void main(String args) {

        List objects = new ArrayList();

        objects.add("string");

        objects.add(1L);


        List<String> onlyStrings = objects;


    }

如果您愿意,您可以将非通用列表转换为 List(您会收到一堆编译器警告)。


查看完整回答
反对 回复 2021-11-11
?
鸿蒙传说

TA贡献1865条经验 获得超7个赞

考虑以下示例:


public static void main(String[] args) {

    List a = new ArrayList();

    a.add("a");

    a.add(1L);


    List<String> b = new ArrayList(a);


    System.out.println(b);

}

这输出


[a, 1]

以上是合法的,因为java执行类型擦除,从而在运行时丢弃元素类型信息。这意味着每个 List 本质上都是一个 List 并且可以包含多个类实例。


public interface List<E> extends Collection<E>

转换为


public interface List extends Collection

在编译时。


如果 E 被绑定,即 E 扩展了 MyClass,它将被转换为 MyClass。


在您的情况下,您正在为每一行实例化一个地图,没有类型界限,您正在对其进行平面映射,然后将其收集到一个列表中,这实际上与我的示例相同,因此是合法的。


查看完整回答
反对 回复 2021-11-11
  • 3 回答
  • 0 关注
  • 194 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信