Java映射函数抛出非静态方法编译器错误

Dan*_*ner 6 java spring dictionary hibernate jpa

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

长话短说:

我有一个设计缺陷,其中...

这有效:

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

但这并不:

this.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

问题 第二个版本中显示的错误是“无法从静态上下文引用非静态方法”。

长版本:

对象模型:该模型由特定于业务类型的订单(例如股票交易、支付)组成,这些订单通过“InheritanceType.JOINED”继承策略从订单实体继承。父订单可以使用该订单的业务类型特定的 DTO 对象进行参数化,例如 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<>();

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

DTO订单:

public class DtoOrder<OrderType extends Order> extends DtoEntity {
Run Code Online (Sandbox Code Playgroud)

DTO 订单 - 业务类型具体示例

public class DtoOrderStex extends DtoOrder<OrderStex> {
Run Code Online (Sandbox Code Playgroud)

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

现在是棘手的部分:DTOOrder 类有一个构造函数,它填充所有业务类型通用的字段,例如流程状态转换列表、订单在其生命周期中经历的情况(已下达、已取消、已执行等......) 。继续使用流程状态转换的示例,这些也被建模为数据库中的 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());
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

rac*_*man 3

感谢您提出一个有趣的问题,它显示了一些意外的行为,即根据规范进行行为。

\n

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

\n
public class DtoOrder<OrderType extends Order<?>> extends DtoEntity {\n
Run Code Online (Sandbox Code Playgroud)\n

这将使构造函数中的 all-in-one-line 方式工作 \xe2\x80\x8b:

\n
\xe2\x80\x8bthis.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().map(OrderExtnTrans::toDto).collect(Collectors.toList());\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
public class Order<DtoOrderType extends DtoOrder> \n
Run Code Online (Sandbox Code Playgroud)\n

通过不指定泛型类型,您可以将其声明OrderTyperaw-type

\n

一般来说,我们习惯于List<SomeType>泛型,在运行时会进行类型擦除。此外,如果我们有旧代码,例如:

\n
List myRawTypeVariable = new ArrayList();\n
Run Code Online (Sandbox Code Playgroud)\n

myRawType是一个原始类型,因为我们可以添加任何Object并且只能取出Objects 。

\n

然而,事实证明(正如您所发现的)原始类型比这更进一步,并且类型擦除也具有编译时影响。

\n

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

\n
\n

原始类型的构造函数 (\xc2\xa78.8)、实例方法 (\xc2\xa78.4、\xc2\xa79.4) 或\n非静态字段 (\xc2\xa78.3) 的类型不是从其超类或超接口继承的 C 是与 C 对应的泛型声明中其类型的擦除相对应的原始类型。

\n
\n

请注意,这并不将类型擦除仅限于泛型类型;所有实例方法的类型都采用其原始类型!

\n

换句话说,通过不指定 的泛型类型Order,您将创建Order一个原始类型 - 因此关闭该类的所有泛型类型检查(除了继承或接口中另外指定的方法等)。

\n

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

\n

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

\n
   \xe2\x80\x8bthis.dtoOrderExtnTransList = orderType.getOrderExtnTransList().stream().peek(s -> s.\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

反过来,这意味着当它点击 时.map(OrderExtnTrans::toDto),而不是将其解释为OrderExtnTrans顺流而下:

\n
 `.map(o -> o.toDto())`, \n
Run Code Online (Sandbox Code Playgroud)\n

它认为你的意思是

\n
  `.map(o -> OrderExtnTrans.toDto(o))` \n
Run Code Online (Sandbox Code Playgroud)\n

这就是适合Object顺流而下的方法 - 这就是为什么它抱怨它toDto是一个非静态方法。

\n

如上所述,解决方案就是不将其视为Order原始类型,而是通过添加上面的 <?> 使其通用。

\n