Generic BiFunction that accepts generic wildcard

徘徊边缘 提交于 2020-08-24 04:22:06

问题


I have Enum and want to have a generic function that could accept any class that implements a maker interface.

public interface IComponentDataExporter { // Marker Interface }

public class ComponentsDataExporter implements IComponentDataExporter { 
  public ComponentTeaser populateFeatured(ComponentTeaserConfiguration componentConfig) { return null; } 
}
public class BussinessComponentsDataExporter 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;
  }
}

I am getting this compilation error The type ComponentsDataExporter does not define populateFeatured(IComponentDataExporter, ComponentTeaserConfiguration) that is applicable here

My main issue is BIFunction I want it to accepts either ComponentsDataExporter or BusinessComponentsDataExporter that's why I tried to use a generic wildcard (? extends IComponentDataExporter and ? super IComponentDataExporter). It is working fine if I replace wildcard with a specific class.


Edit

Sorry for not being clear in my questation so I will try to explain more and little of history.

I want to force whoever adding a new enum value to provide a method which is exporting data for that component. Logic of export method cannot be placed in Enum as it requires access to spring managed beans and may have a lot of business logic.

I already did that and it was working perefectly, you can have a look on this demo.

My problem started when I noticed that having all of export methods in one class will be too much so I decided to have different kind of implementation (BussinessComponentsDataExporter and ComponentsDataExporter) each one of them handles set of components.

To do that speration I have added a marker interface so I can mark any exporter class and updated BiFunction in Enum to have wildcard so it can accpet any kind of object that implements my marker interface.

BiFunction<? super IComponentDataExporter, ComponentTeaserConfiguration, ComponentTeaser> exporter

The solutions I am working on right now is reverting to my old version of BiFunction which was like the demo

BiFunction<ComponentsDataExporter, ComponentTeaserConfiguration, ComponentTeaser>

Considering ComponentsDataExporter as Facade which have instances of different kind of exporters where the whole exporting logic resides (businessExporter, contentExporter ..etc) so facade will have all high levels methods that can be called from Enum. Here's example for the solution I am considering right now


回答1:


Read carefully what your error message says…

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

BiFunction<T,U,R> is defined to take two input arguments. But you define populateFeatured(ComponentTeaserConfiguration) with only one parameter…

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

So you need to could rewrite it like I do here…

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

Notice it's static in my demo. If you want to use a method reference (FEATURED(ComponentsDataExporter::populateFeatured)) then the method either has to be static or you need an object instance to call it if it's not static (new ComponentsDataExporter()::populateFeatured).

And then the BiFunction changes to…

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

An upper bound wildcard (? extends IComponentDataExporter) as the first type argument to your BiFunction wouldn't compile. A lower bound wildcard (? super IComponentDataExporter) would actually work there. But it would be confusing. It's better off being specified as simply a normal interface type argument (IComponentDataExporter).

That would then be used like…

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

Click the green Start button at the top of the demo to see it run.


EDIT: If I understand your follow-up comments correctly, I don't think you want a BiFunction at all.

From what I gather from your comments — and your original implementation of populateFeatured(...) — if seems like what you actually want is a Function…

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

Because your questions and comments are not crystal clear, these suggestions are based on what I assume your intention is.

…the call will be something like this ComponentTypeEnum.Featured.exporter.apply(exporterInstance, configObj)

One of the things that you have not made crystal clear is the reason why you need to pass a IComponentDataExporter into populateFeatured(...), given that ComponentsDataExporter is itself a concrete implementation of IComponentDataExporter. Please update your question with something that clarifies that?



来源:https://stackoverflow.com/questions/63323381/generic-bifunction-that-accepts-generic-wildcard

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!