简体   繁体   中英

Generic BiFunction that accepts generic wildcard

I have an 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 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;
  }
}

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 ( BusinessComponentsDataExporter and ComponentsDataExporter ) each one of them handles set of components.

To do that separation I have added a marker interface so I can mark any exporter class and updated BiFunction in Enum to have wildcard so it can accept 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

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?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM