[英]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
的成员,而不是endsWith
等String
的成员。
反过来,这意味着当它点击.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.