猿问

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

昨天我偶然发现了一些我无法解释的奇怪的 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

@Entity

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

}

这就是事情变得混乱的地方。我编写了一个简单的单元测试来检查值,但结果在第二次断言时失败:


@Test

public 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


30秒到达战场
浏览 196回答 3
3回答

繁星点点滴滴

这是可能的,因为 Java 中的类型擦除。简而言之,这意味着在运行时 Java 不知道您的泛型类型,因此任何List对象都使用Object类型进行操作。泛型具有编译类型安全性。但是您可以更详细地了解类型擦除,例如这里。您可以自己模仿的相同行为:public static void main(String[] args) {&nbsp; &nbsp; List<String> list = new ArrayList<>();&nbsp; &nbsp; addToList(list);&nbsp; &nbsp; System.out.println(list);}private static void addToList(List list) {&nbsp; &nbsp; list.add(1L);&nbsp; &nbsp; list.add(42);&nbsp; &nbsp; list.add("String");&nbsp; &nbsp; list.add(new Runnable() {&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public void run() {}&nbsp; &nbsp; });}只要您不尝试将列表条目作为字符串进行操作,此代码就可以正常工作。但是当你添加如下内容时:for (String s : list) {&nbsp; &nbsp; System.out.println(s);}你会得到java.lang.ClassCastException。因此,在编译时,您可以使用List<String>,但在运行时,Java 只知道List.

幕布斯7119047

是的,你可以,这是一个小例子public static void main(String args) {&nbsp; &nbsp; &nbsp; &nbsp; List objects = new ArrayList();&nbsp; &nbsp; &nbsp; &nbsp; objects.add("string");&nbsp; &nbsp; &nbsp; &nbsp; objects.add(1L);&nbsp; &nbsp; &nbsp; &nbsp; List<String> onlyStrings = objects;&nbsp; &nbsp; }如果您愿意,您可以将非通用列表转换为 List(您会收到一堆编译器警告)。

鸿蒙传说

考虑以下示例:public static void main(String[] args) {&nbsp; &nbsp; List a = new ArrayList();&nbsp; &nbsp; a.add("a");&nbsp; &nbsp; a.add(1L);&nbsp; &nbsp; List<String> b = new ArrayList(a);&nbsp; &nbsp; 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。在您的情况下,您正在为每一行实例化一个地图,没有类型界限,您正在对其进行平面映射,然后将其收集到一个列表中,这实际上与我的示例相同,因此是合法的。
随时随地看视频慕课网APP

相关分类

Java
我要回答