【问题标题】:Generic BiFunction that accepts generic wildcard接受通用通配符的通用 BiFunction
【发布时间】:2020-11-29 01:34:11
【问题描述】:

我有一个枚举,并希望有一个通用函数,它可以接受任何实现制造商接口的类。

public interface IComponentDataExporter { // Marker Interface }

public class ComponentsDataExporter implements IComponentDataExporter { 
  public ComponentTeaser populateFeatured(ComponentTeaserConfiguration componentConfig) { return null; } 
}
public class BusinessComponentsDataExporter implements IComponentDataExporter { // methods } 

public enum ComponentTypeEnum { 
  FEATURED(ComponentsDataExporter::populateFeatured); 
  
  public final BiFunction<? super IComponentDataExporter, ComponentTeaserConfiguration, ComponentTeaser> exporter;

  private ComponentTypeEnum( BiFunction<? super IComponentDataExporter, ComponentTeaserConfiguration, ComponentTeaser> exporter) {
  this.exporter = exporter;
  }
}

我收到此编译错误The type ComponentsDataExporter does not define populateFeatured(IComponentDataExporter, ComponentTeaserConfiguration) that is applicable here

我的主要问题是BIFunction 我希望它接受ComponentsDataExporterBusinessComponentsDataExporter 这就是为什么我尝试使用通用通配符(?扩展IComponentDataExporter 和?超级IComponentDataExporter)。如果我用特定的类替换通配符,它​​工作正常。


编辑

抱歉,我的问题不清楚,所以我将尝试解释更多和更少的历史。

我想强制任何添加新枚举值的人提供一种为该组件导出数据的方法。导出方法的逻辑不能放在 Enum 中,因为它需要访问 spring 托管的 bean,并且可能有很多业务逻辑。

我已经这样做了,而且效果很好,你可以看看这个demo

当我注意到在一个类中包含所有导出方法会太多时,我的问题就开始了,所以我决定使用不同类型的实现(BusinessComponentsDataExporterComponentsDataExporter),每个实现都处理一组组件。

为了做到这一点,我添加了一个标记接口,这样我就可以标记任何导出器类,并将 Enum 中的 BiFunction 更新为具有通配符,这样它就可以接受任何类型的实现我的标记接口的对象。

BiFunction<? super IComponentDataExporter, ComponentTeaserConfiguration, ComponentTeaser> exporter

我现在正在研究的解决方案是恢复到我的旧版本 BiFunction,就像演示一样

BiFunction<ComponentsDataExporter, ComponentTeaserConfiguration, ComponentTeaser>

考虑到ComponentsDataExporter 作为外观,它具有不同类型的导出器的实例,整个导出逻辑都驻留在其中(businessExportercontentExporter 等),因此外观将具有可以从 Enum 调用的所有高级方法。这是我现在正在考虑的解决方案的example

【问题讨论】:

  • 什么是特色?
  • 这是枚举值之一,这个枚举有不同的值(精选和许多其他)。我想为枚举中的每个项目设置出口商 BIFunction。
  • 哦,我明白了,这太棒了。也许尝试修复您的拼写错误,看看它是否有助于解决编译错误? this.compoenentDataExporter=componentDataExporter - 你有时会正确调用组件,有时会出现拼写错误
  • 这里使用通配符没有任何意义,它们并没有改善任何东西。你必须修复你的意图的根本缺陷:你有一个没有声明任何方法的 marker 接口,但是想要使用一个假定特定方法 always 的方法引用i> 在所有实现中。但是标记接口并不能保证该方法存在。有简单的解决方案,例如如果假定所有实现都具有该方法,则在接口中声明该方法。
  • @deduper 这可能是本意,但是那样做是不可能的。您不能使用仅适用于特定实现类型的方法引用来初始化承诺处理标记接口的所有实现的函数。

标签: java generics java-8 enums functional-programming


【解决方案1】:

仔细阅读你的错误信息……

„...ComponentsDataExporter does not define populateFeatured(IComponentDataExporter, ComponentTeaserConfiguration)...“

BiFunction&lt;T,U,R&gt; is defined to take two input arguments。但是您定义 populateFeatured(ComponentTeaserConfiguration) 时只有 一个 参数...

...
public ComponentTeaser populateFeatured(ComponentTeaserConfiguration componentConfig)...
...

所以你需要 可以重写它like I do here...

...
public static ComponentTeaser populateFeatured( IComponentDataExporter self, ComponentTeaserConfiguration componentConfig )... 
...

注意它是 static in my demo。如果您想使用方法引用 (FEATURED(ComponentsDataExporter::populateFeatured)),则该方法必须是 static 或者如果不是 ,您需要一个对象实例来调用它em>static (new ComponentsDataExporter()::populateFeatured)。

然后 BiFunction 变为……

...
BiFunction< IComponentDataExporter, ComponentTeaserConfiguration, ComponentTeaser >...
...

上限通配符 (? extends IComponentDataExporter) 作为 BiFunction 的第一个类型参数将无法编译。下限通配符 (? super IComponentDataExporter) 实际上可以在那里工作。但这会令人困惑。最好将其指定为简单的普通接口类型参数 (IComponentDataExporter)。

然后会像……一样使用它

ComponentTeaserConfiguration conf = null;
    
BusinessComponentsDataExporter exp = null;
    
ComponentTypeEnum featured = ComponentTypeEnum.FEATURED;
    
ComponentTeaser teaser = featured.export( exp, conf );
    
teaser.tease( );

单击演示顶部的绿色开始按钮以查看它的运行情况。


编辑:如果我正确理解您的后续 cmets,我认为您根本不想要 BiFunction

根据我从您的 cmets 收集到的信息 - 以及您对 populateFeatured(...) 的原始实现 - 如果您实际上想要的是 Function...

...
Function< ComponentTeaserConfiguration, ComponentTeaser >...
...

由于您的问题和 cmets 不是很清楚,因此这些建议是基于我假设您的意图。

...电话将类似于 ComponentTypeEnum.Featured.exporter.apply(exporterInstance, configObj)...

您还没有说清楚的一件事是您需要将 IComponentDataExporter 传递给 populateFeatured(...) 的原因,因为 ComponentsDataExporter 本身IComponentDataExporter的具体实现。请用一些东西来更新您的问题,以澄清这一点?

【讨论】:

  • 感谢您的详细解答。我使用带有一个参数的 populateFeatured,因为第一个参数将用作实例,因此调用将类似于 ComponentTypeEnum.Featured.exporter.apply(exporterInstance, configObj);。如果我在 BIFunction 中使用特定类(ComponentDataExporter 或 BusinessExporter),这实际上是有效的。对我来说,在所有填充方法中继续传递自引用是没有用的,我认为只在调用 apply 时传递就足够了。
  • 不清楚你的意思是什么:“我使用带有一个参数的 populateFeatured,因为第一个参数将用作实例”。 The rules say 任何用作BiFunction的方法必须采用两个论据。 BiFunction 中的前缀 Bi 表示 两个。 —“如果我使用特定的类,这实际上是有效的”——请发布一个这样的“实际工作”代码的简单示例?试试Ideone。你会教我一些我从来不知道甚至可能的东西。所以 TIA。
  • 参见例如BiFunction&lt;String,Charset,byte[]&gt; f = String::getBytes;getBytes(Charset) 方法只有一个参数,但您将在其上调用它的 String 对象将成为 BiFunction 的第一个参数。
  • 啊!好的。谢谢@Holger。每天学习新东西:)在阅读您的评论之前,我正在编辑我的答案。我回去并相应地编辑了我的编辑,知道你刚刚解释了什么。再次感谢。
  • @AhmedYousef 我changed my demo 反映了我在回答中所做的编辑。再次,pleasesomething 更新您的问题,阐明为什么需要传递 的实例IComponentDataExporter 变成同一个IComponentDataExporter的方法调用???还是我也误解了您对此的解释?如果您的设计与您一样不明确且非正统,您可以分享的信息越多,提供的答案就会越好。
猜你喜欢
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-04
  • 1970-01-01
相关资源
最近更新 更多