繁体   English   中英

Java map function 抛出非静态方法编译器错误

[英]Java map function throws non-static method compiler error

我有一个奇怪的问题,尽管关于该主题有许多 SO 问题,但我仍在努力理解 Java 中“静态上下文”的性质。

TL;博士:

我有一个设计缺陷,在哪里...

这有效:

List<OrderExtnTrans> list = orderType.getOrderExtnTransList();
this.dtoOrderExtnTransList = list.stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());

但这不会:

this.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());

问题 第二个版本显示的错误是“Non-static method cannot be referenced from a static context”。

长版:

Object Model: The model consists of Business Type specific orders (eg. Stock exchange, payments), which inherit from a an order entity via an "InheritanceType.JOINED" inheritance strategy. 父订单可以使用该订单的特定业务类型 DTO object 进行参数化,例如 DtoStockExchangeOrder。 这是为了启用,JPA 对象可以映射到实体内的 DTO 等效项,而不是在服务中(我之前做过。它有效,但它“不太干净”)。

JPA 订购:

@Entity
@Table(name = "ORDER_BASE")
@Inheritance(strategy = InheritanceType.JOINED)
public class Order<DtoOrderType extends DtoOrder> implements Serializable {

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "order", orphanRemoval = true)
    private List<OrderExtnTrans> orderExtnTransList = new ArrayList<>();

}

JPA 订单 - 业务类型具体示例:

@Entity
@Table(name = "ORDER_STEX")
@Inheritance(strategy = InheritanceType.JOINED)
public class OrderStex extends Order<DtoOrderStex> implements Serializable {

同样,DTO 订单遵循相同的模式,可以使用特定于业务类型的 JPA 实体对其进行参数化,以启用相关映射:

DTO 订单:

public class DtoOrder<OrderType extends Order> extends DtoEntity {

DTO 订单 - 业务类型特定示例

public class DtoOrderStex extends DtoOrder<OrderStex> {

它继承的 DTOEntity class 只是一个“包装” class,由一个 ID 和一个名称组成。

现在是棘手的部分:DTOOrder class 有一个构造函数,它填充所有业务类型共有的字段,例如流程状态转换列表,订单在其生命周期中经历(下达、取消、执行等) . 继续以进程状态转换为例,这些也被建模为数据库中的 JPA 实体,以及它们对应的 DTO 对应物(同样参数化,该部分工作正常)。

这里的构造函数:

public DtoOrder(OrderType orderType) {
    super(orderType);
    // this is the part from above, which works (but it shows a warning: Unchecked assignment: 'java.util.List' to 'java.util.List<com.tradingvessel.core.persistence.model.OrderExtnTrans>' )
    List<OrderExtnTrans> list = orderType.getOrderExtnTransList();
    this.dtoOrderExtnTransList = list.stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());
    // this is how I would have expected it to work, but it does not, with the error shown above: "Non-static method cannot be referenced from a static context"
    this.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());
}

如果我注释掉非工作版本,应用程序将按预期运行并且不会引发错误,因此“逻辑上”,这是可行的。 但是 JAVA 不允许它在第二个版本中开发。

如果我使用“Order”而不是 OrderType,它也可以正常工作,但显然会在其他地方抛出错误,因为构造函数的签名发生了变化。 我假设另一种方法是根据构造函数的调用者参数化方法或参数化父 class DtoOrder 以了解子 class 的类型,但应该有更好的方法吗?

我做错了什么,为什么上版本按预期工作?

解决此问题的一种方法是使用自己的子代参数化 ParentDTO Class。

public class DtoOrderStex extends DtoOrder<OrderStex, DtoOrderStex> {


    public DtoOrderStex(OrderStex orderStex) {
        super(orderStex);
    }

    public DtoOrderStex() {
    }
}

这就提出了一个问题:除了子类的冗余之外,这是否有任何严重的负面影响? 考虑到 class 已经是其父级的子级,为什么这首先是必要的?

感谢您提出一个有趣的问题,该问题显示了一些意外行为,即按照规范行事。

TL;DR(即快速轻松地解决此问题的正确方法)是将<?>通用通配符添加到 DtoOrder class 声明中的 Order 中,因此:

public class DtoOrder<OrderType extends Order<?>> extends DtoEntity {

这将使构造函数中的一体化方式工作:

​this.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());

至于为什么这是修复,这是因为您已将 Order 定义为 Generic 类型:

public class Order<DtoOrderType extends DtoOrder> 

通过不指定泛型类型,您将其声明(因此也将OrderType设为)为raw-type

通常我们习惯List<SomeType>是通用的,在运行时会进行类型擦除。 此外,如果我们有旧代码,例如:

List myRawTypeVariable = new ArrayList();

myRawType是原始类型,因为我们可以添加任何Object并且只得到Object

但是,事实证明(正如您所发现的)原始类型 go 比这更进一步,并且类型擦除也具有编译时含义。

Java 语言规范 (JLS) 这么说(来源: https://docs.oracle.com/javase/specs/jls/jls-4/javase/specs/jls/jls-4/

未从其超类或超接口继承的原始类型 C 的构造函数(第 8.8 节)、实例方法(第 8.4 节、第 9.4 节)或非静态字段(第 8.3 节)的类型是对应于的原始类型在对应于 C 的通用声明中擦除其类型。

请注意,这并不是将类型擦除仅限于泛型类型; 所有实例方法的类型都被带到它们的原始类型!

换句话说,通过不指定Order的泛型类型,您使Order成为原始类型 - 因此关闭该 class 的所有泛型类型检查(除了方法等,另外指定,来自 inheritance 或接口)。

因此,即使getOrderExtnTransList()被声明为返回List<String> ,因为您使用Order作为原始类型,它也会删除<String>泛型并将该方法视为简单地返回List (实际上是List<Object> )。

您可以通过尝试插入peek来确认这一点,因此:

   ​this.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().peek(s -> s.

然后尝试进行自动完成。 您会发现选项只是Object的成员,而不是endsWithString的成员。

反过来,这意味着当它点击.map(OrderExtnTrans::toDto)时,而不是为从OrderExtnTrans下来的 OrderExtnTrans 解释这个:

 `.map(o -> o.toDto())`, 

它认为你的意思是

  `.map(o -> OrderExtnTrans.toDto(o))` 

这适用于Object下降到 stream - 这就是为什么它抱怨toDto是一种非静态方法的原因。

如前所述,解决方案是简单地不将Order视为原始类型,而是通过添加 <?> 使其成为通用类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM