【问题标题】:Why can't i assign a lambda to an untyped predicate reference? but can assign an initialized typed predicate reference to it?为什么我不能将 lambda 分配给无类型的谓词引用?但是可以为其分配一个初始化的类型谓词引用吗?
【发布时间】:2018-07-26 16:50:01
【问题描述】:

我在尝试创建基于方法参数类型使用的自定义谓词引用时发现了一些奇怪的东西。

我有一个名为AffiliateLinkSubset 的对象,它有一个名为isGeneral 的布尔getter。当我尝试执行以下操作时:

Predicate<?> partitionPredicate = AffiliateLinkSubset::isGeneral;

我收到错误non-static method cannot be referenced from a static context

但是当我将泛型类型AffiliateLinkSubset 分配给它工作的谓词时,这没什么特别的。但特别的是,以下内容也可以工作:

Predicate<AffiliateLinkSubset> partitionPredicate = affiliateLinkSubset::isGeneral;
Predicate<?> test = partitionPredicate;

IDE 没有给出任何错误!即使我有效地将相同的 lambda 分配给无类型谓词测试。这怎么可能?谓词会在运行时起作用吗?我认为它会因为在编译期间所有类型都被删除并替换为 Object 类型。这就是为什么我不明白为什么我不能为无类型谓词分配 lambda。谁能解释一下?

AffiliateLinkSubset是实际类的缩写,这里是:

import POJOs.PojoENUMS.LocalizedStorefront;

import java.util.Map;
import java.util.Set;

public class AffiliateLinkSubsetForStatisticsCalculation {
    private Long id;
    private String title;
    private Double productValue;
    private boolean general;
    private Set<String> keywords;
    private Map<String, Boolean> booleanKeywords;
    private LocalizedStorefront localizedStorefront;

    public AffiliateLinkSubsetForStatisticsCalculation(Long id, String title, Double productValue, boolean general, Set<String> keywords, Map<String, Boolean> booleanKeywords, LocalizedStorefront localizedStorefront) {
        this.id = id;
        this.title = title;
        this.productValue = productValue;
        this.general = general;
        this.keywords = keywords;
        this.booleanKeywords = booleanKeywords;
        this.localizedStorefront = localizedStorefront;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Double getProductValue() {
        return productValue;
    }

    public void setProductValue(Double productValue) {
        this.productValue = productValue;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isGeneral() {
        return general;
    }

    public void setGeneral(boolean general) {
        this.general = general;
    }

    public Set<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(Set<String> keywords) {
        this.keywords = keywords;
    }

    public Map<String, Boolean> getBooleanKeywords() {
        return booleanKeywords;
    }

    public void setBooleanKeywords(Map<String, Boolean> booleanKeywords) {
        this.booleanKeywords = booleanKeywords;
    }

    public LocalizedStorefront getLocalizedStorefront() {
        return localizedStorefront;
    }

    public void setLocalizedStorefront(LocalizedStorefront localizedStorefront) {
        this.localizedStorefront = localizedStorefront;
    }
}

【问题讨论】:

  • 请向我们展示AffiliateLinkSubset 类及其方法isGeneral
  • 没有一个方法是静态的
  • @Maurice Predicate&lt;?&gt; p = ffiliateLinkSubset::isGeneral; 工作吗?
  • @Lino 它不是静态上下文,当 IDE 没有任何其他标准错误消息要显示时抛出此错误,它没有说明实际错误的性质。 ffiliateLinkSubset::isGeneral 是一个方法引用,或者可以说是一个 lambda 缩写。在这里查看更多信息:dzone.com/articles/java-lambda-expressions-vs
  • @Maurice 您可以选择从概念上将方法引用视为 lambda 表达式的语法糖,但 the code generated is not the same,并且方法引用不会“脱糖”变成一个 lambda。当您提供的代码使用方法引用而不是 lambda 时,在这篇文章的标题、正文和标签中使用“lambda”是错误的。将方法引用称为 lambda 是不正确且错误的。

标签: java generics lambda java-8 functional-interface


【解决方案1】:

首先Predicate&lt;?&gt; predicate 无法工作,因为未知类型。它们中的任何一个都可以工作:

Predicate<? extends AffiliateLinkSubset> predicate = AffiliateLinkSubset::isGeneral;
Predicate<AffiliateLinkSubset> predicate = AffiliateLinkSubset::isGeneral;

方法引用快捷方式AffiliateLinkSubset::isGeneral理解为object -&gt; object.isGeneral(),其中objectAffiliateLinkSubset的一个类型。这就是为什么第一个谓词不能工作的原因,因为&lt;?&gt; 没有定义AffiliateLinkSubset 的类型。 isGeneral() 方法未在 Object 中定义。

让我们继续。如果你输入:

Predicate<?> partitionPredicate = object -> AffiliateLinkSubset.isGeneral();

这不会编译,因为您将类方法isGeneral() 视为静态方法。为此,您需要一个可以调用此方法的类的实例:

AffiliateLinkSubset affiliateLinkSubset = new AffiliateLinkSubset();
Predicate<?> partitionPredicate = object -> affiliateLinkSubset.isGeneral();

现在,objectObject 的一个实例,结果依赖于 lambda 的右侧,它不接触 object 并且与输入无关,因此这可能也有效:

Supplier<Boolean> supplier = () -> affiliateLinkSubset.isGeneral();

这没有多大意义,所以我猜你已经提到过的解决方案:

Predicate<? extends AffiliateLinkSubset> predicate = AffiliateLinkSubset::isGeneral;

【讨论】:

  • 如果我输入第一个谓词,我可以不输入第二个谓词 (>)。当我将第一个谓词分配给第二个无类型谓词时,我没有收到错误,为什么会这样?第二个谓词赋值不应该也报错吗?
【解决方案2】:
AffiliateLinkSubsetForStatisticsCalculation::isGeneral 

意思

(AffiliateLinkSubsetForStatisticsCalculation i) -> i.isGeneral()

同时

affiliateLinkSubsetForStatisticsCalculationInstance::isGeneral

指的是(1)AffiliateLinkSubsetForStatisticsCalculation 的实例方法boolean isGeneral(Object o) 或(2)Object 的实例方法boolean isGeneral()

由于这两种方法都不存在,您可以编写方法引用。但是你可以写一个 lambda:

Predicate<?> p = (Object i) -> affiliateLinkSubsetForStatisticsCalculationInstance.isGeneral();

*AffiliateLinkSubsetForStatisticsCalculation 是类名。
**affiliateLinkSubsetForStatisticsCalculationInstance 是该类的一个实例。

【讨论】:

  • 类的变量是什么意思,比如类的实例?
  • 很好的答案,但它仍然没有回答主要问题。为什么我可以将类型谓词分配给无类型谓词引用?是因为通配符吗?可以翻译为对象,并且由于我的 AffiliateSubset 类最终也是一个对象,因此可以通过被动转换来分配它?例如,您可以始终将字符串转换为对象引用,因为字符串始终是对象。
  • @Maurice,你说得对,Predicate&lt;?&gt; 代表“任何事物的谓词”,Predicate&lt;AffiliateLinkSubsetForStatisticsCalculation&gt; 符合这一点
猜你喜欢
  • 2018-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-24
相关资源
最近更新 更多