问题
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