Issue while using generic - java

I have below Genric class which is being used to set any custom data type
public class NotificationData<E> {
private E element;
public E getElement() {
return element;
}
Below Notification task interface which is showing a compile time error -
T cannot be resolved to a type. I add "" in front of the method it does resolve error but creating issue for other classes which are using this interface.
public interface NotificationTask {
void execute(NotificationData<T> taskData);
// --other methods
}
Below class implements its but showing error
Name clash: The method execute(NotificationData) of type
AbstractEmailNotificationTask has the same erasure as
execute(NotificationData) of type NotificationTask but does not
override it
and prepareEmailTemplate show following error-
The method prepareEmailTemplate(NotificationData) from the type
AbstractEmailNotificationTask refers to the missing type T
public abstract class AbstractEmailNotificationTask implements NotificationTask{
private static final Log logger = LogFactory.getLog(AbstractEmailNotificationTask.class);
private boolean flag;
public <T> void execute(NotificationData<?> taskData) {
try {
String content=prepareEmailTemplate(taskData);
setTaskExceuted(true);
} catch (Exception e) {
logger.debug (e.getMessage (),e);
setTaskExceuted(false);
}
}
abstract protected String prepareEmailTemplate(NotificationData<T> taskData) throws TaskExecutionException;
}
public class AddressUpdateEmailNotification extends AbstractEmailNotificationTask {
public AddressUpdateEmailNotification() {
}
#Override
protected String prepareEmailTemplate(NotificationData<CustomerAddress> taskData) {
CustomerAddress customerAddress= taskData.getElement();
return customerAddress.getCity() +":"+customerAddress.getState().getStateName();
}
}
Above is actual class which will implement prepareEmail template. In this class I am passing CustomerAddress but in other Classes which are extending AbstractEmailNotificationTask, I have to pass other object. I want to use generic but facing these issues.

Your interface is not generic, thus the methods inside it are not recognized with the generic type (and cannot be resolved to one). Simply make it generic:
public interface NotificationTask<T> {
void execute(NotificationData<T> taskData);
// --other methods
}

Related

Implement a parameterized interface method that has a parameterized class argument with a concrete class

public interface AssignmentInterface<T> {
MetadataFieldInterface<T> getField();
void setField(MetadataFieldInterface<T> field);
}
public class DatasetAcquisitionAssignment implements AssignmentInterface<DatasetAcquisition> {
#Override
public DatasetAcquisitionMetadataField getField() {
...
}
#Override
public void setField(DatasetAcquisitionMetadataField field) { // HERE !!!
...
}
public enum DatasetAcquisitionMetadataField implements MetadataFieldInterface<DatasetAcquisition> { ... }
Eclipse error : The method setField(DatasetAcquisitionMetadataField) of type DatasetAcquisitionAssignment must override or implement a supertype method
Compilation fails as well
Using public void setField(MetadataFieldInterface<DatasetAcquisition> field) {} instead fixes the problem (but leads to further problems at runtime, with jackson)
What can I do so I can use DatasetAcquisitionMetadataField as the concrete method argument ?
Can you change your interface to?
public interface AssignmentInterface<T, R extends MetadataFieldInterface<T>>{
MetadataFieldInterface<T> getField();
void setField(R field);
}

Java generics both methods have same erasure error

In my project, I have multiple services performing three basic operations - create, edit and search. For this, I am trying to create a generic service. Below is what I have come up with so far.
Search method will take a list of objects at runtime.
public interface GenericService<T> {
void update(T t);
void create(T t);
T search(List<?> t);
}
Also, I have created an abstract class where the common code for all services will be placed.
public abstract class AbstractService<T> implements GenericService<T> {
}
Here is my implementation
public class AccountService extends AbstractService<Account> implements GenericService<Account> {
#Override
public void update(Account account) { }
#Override
public void create(Account account) { }
#Override
public Account search(List<SearchCriteria> t) { return null; }
}
Here are my Account and SearchCriteria classes
public class Account {
private String accountNumber;
private Date openingDate;
// more fields
// getter setter removed for brevity
}
Search criteria class
public class SearchCriteria {
private String key;
private String value;
// getter setter removed for brevity
}
Problem: on line public Account search(List t) { return null; }, getting compilation error saying
'search(List)' in
'com.test.AccountService' clashes with
'search(List)' in 'com.test.GenericService';
both methods have same erasure, yet neither overrides the other
In order for
public Account search(List<SearchCriteria> t) { ...}
to override
T search(List<?> t);
The arguments must be the same after type parameter substitution, but ? is not SearchCriteria.
Therefore, if you want to keep these methods (the inheritance looks a bit wild to me), you'll need to parameterise the types further.
public interface GenericService<T, C> {
// ...
T search(List<C> t); // probably change that parameter name
}
public abstract class AbstractService<T, C>
implements GenericService<T, C>
{
}
public class AccountService
extends AbstractService<Account, SearchCriteria>
implements GenericService<Account, SearchCriteria> // unnecessary
{
// ...
#Override
public Account search(List<SearchCriteria> t) { /* ... */ }
}
Changing List<?> to List<SearchCriteria> in GenericService will solve the error. There is no benefit in using a wildcard if the search method will always take a list of SearchCriteria objects in every service implementation.
If, however, you want to make this generic as well, you can introduce a second type parameter.

How to make an interface extends its own generic type

I would like to create a contract (an interface), with a generic parameter, which enforces that the implemented class must also be the type specified in the generic parameter.
public interface SelfDefaultAlternativeDetailSupport<T extends AlternativeDetail> extends T { // syntax error (extends T)
default T resolveDetail() {
if (someConditions()) {
return this;
} else {
return getAlternativeDetails().stream()
.filter(somePredicate)
.findFirst().orElse(null);
}
}
List<T> getAlternativeDetails();
}
Example Usage
public interface CustomerDetail extends AlternativeDetail {
String getName();
}
public class Customer implements SelfDefaultAlternativeDetailSupport<CustomerDetail>, CustomerDetail {
#Override
public String getName() {
return "default name";
}
#Override
public List<AlternativeDetails> getAlternativeDetails() {
...
}
}
In other words, I would like that when a class implements SomeInterface<X>, the class must also implement X, But the attempt above has syntax because I cannot make SelfDefaultAlternativeDetailSupport extends T. Is this possible in Java?
You can make it a self-referential generic type:
public interface SelfDefaultAlternativeDetailSupport<T extends SelfDefaultAlternativeDetailSupport<T> & AlternativeDetail> {
#SuppressWarnings("unchecked")
default T resolveDetail() {
if (someConditions()) {
return (T) this;
} else {
return getAlternativeDetails().stream()
.filter(somePredicate)
.findFirst().orElse(null);
}
}
List<T> getAlternativeDetails();
}
Then you can have:
public class Customer implements SelfDefaultAlternativeDetailSupport<Customer>, CustomerDetail {
#Override
public List<Customer> getAlternativeDetails() {
...
}
}
Just be careful to never use a different class as the parameter to SelfDefaultAlternativeDetailSupport. If you want to avoid the unchecked cast of this, you can use getThis methods, but I don't think it's really worth it.

Generic issues: T extends interface type. Cannot call method to return an object which extends the type?

I have this method declared in a parent class:
protected <ConfigT extends LoadableConfig> ConfigT getConfig(final String configId) {
return (ConfigT)getConfigService().getConfig(configId, getDriver());
}
The configuration service method is defined as follows:
#SuppressWarnings("unchecked")
#Override
public <ConfigT extends LoadableConfig> ConfigT getConfig(String configId, WebDriver driver) {
//noinspection unchecked
Map<String,LoadableConfig> profile = profiles.get(getProfileName(driver));
if(profile != null) {
return (ConfigT) profile.get(configId);
}
return null;
}
The configuration type I want is here:
public interface AccessibleConfig extends LoadableConfig, PolleableConfig {
....
}
This line of code is throwing an incompatible type error:
AccessibleConfig config = getConfig(ValidationPane.class.getCanonicalName());
How is this possible? The AccessibleConfig extends LoadableConfig. The method called declared that that return type is some type which extends LoadableConfig. I am running jdk1.8.0_102.jdk. This failure happens in IntelliJ as well as when compiling with Maven from the command line. I am able to do this with other configuration types which extends the LoadableConfig type.
EDIT:
Parent class:
public abstract class AbstractLoadable<T extends AbstractLoadable<T>> {
private Map<String,LoadableConfig> profiles = new HashMap<>();
protected <T extends LoadableConfig> T getConfig(final String configId) {
return (T) profiles.get(configId);
}
}
Concrete class:
public class ConcreteLoadable extends AbstractLoadable {
public ConcreteLoadable(final String profileName) {
AccessibleConfig config = getConfig(ConcreteLoadable.class.getCanonicalName());
}
}
And the interface types:
public interface LoadableConfig {
Integer getLoadTimeoutInSeconds();
void setLoadTimeoutInSeconds(final Integer loadTimeoutInSeconds);
}
public interface AccessibleConfig extends LoadableConfig {
Boolean getHoverOverAccessorWithJavascript();
void setHoverOverAccessorWithJavascript(final Boolean hoverOverAccessorWithJavascript);
Boolean getClickAccessorWithJavascript();
void setClickAccessorWithJavascript(final Boolean clickAccessorWithJavascript);
}
So, the exercise of producing this minimal example actually made it really easy to identify the source of the compiler error. I have posted an answer to this question below. I humbly accept that I did not in fact originally post a complete example.
I have identified the problem. The concrete class needs to pass itself to the parent because the parent is reflexively generic. The error message wasn't super helpful so I got tied up in thinking about the generic parameters of the parent class method and not of the generic parameters applied to the parent class itself.
The class declaration for ConcreteLoadable needs to changed to this to get rid of the compile error:
public class ConcreteLoadable extends AbstractLoadable<ConcreteLoadable> {
public ConcreteLoadable(final String profileName) {
AccessibleConfig config = getConfig(ConcreteLoadable.class.getCanonicalName());
}
}

Generic types: Capture type of attribute

Suppose I have this codebase:
public class DataIn {}
public interface DataOut {}
public class DataSpecial1 implements DataOut {}
public class DataSpecial2 implements DataOut {}
public class TranslatorAndHandler<T extends DataOut>{
public T translate(DataIn dataIn);
public void handle(T t);
}
public class TranslatorImpl1 implements TranslatorAndHandler<DataSpecial1> {
public DataSpecial1 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial1 data){}
}
public class TranslatorImpl2 implements TranslatorAndHandler<DataSpecial2> {
public DataSpecial2 translate(DataIn dataIn){
// Some code
return null;
}
public void handle(DataSpecial2 data){}
}
public class Wrapper {
public static TranslatorAndHandler<? extends DataOut> getCorrectTAH(){
if(someValue) {
return new TranslatorImpl1();
}
return new TranslatorImpl2();
};
private static final TranslatorAndHandler<? extends DataOut> tah = getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Compiler won't accept this line
}
}
A bit of context: I write a model format (DataIn) that is read by a reader and then passed on the the Model (Wrapper). As different users will have different OpenGL-capabilites I externalized the rendering (TranslatorAndHandler). This would allow me to load the correct OpenGLHandler at runtime (getCorrectTAH()) and then make this Handler translate the read raw data into an optimized format for his own purposes.
Any human can tell that there is no problem with the code and that it is indeed typesafe. How can I remodel (redesign?) my approach so that this will work?
Currently it's showing this compiler error:
The method handle(capture#1-of ? extends DataOut) in the type
TranslatorAndHandler is not applicable
for the arguments (DataOut)
You pass an instance of type DataOut to tah.handle, however, it expects some subtype of it - for example, it should be clear that the following code should not compile:
interface DataOut { }
interface SomeDataOut extends DataOut { }
class TranslatorAndHandler<T extends DataOut> {
public void handle(T t) { }
}
public class Main {
public static void main(String[] args) {
TranslatorAndHandler<SomeDataOut> tah = null;
DataOut t = null;
tah.handle(t);
}
}
And it indeed causes a compilation error:
incompatible types: DataOut cannot be converted to SomeDataOut
A possible fix is making Wrapper class generic like this:
public class Wrapper<T extends DataOut> {
private final TranslatorAndHandler<T> tah = getCorrectTAH();
private T savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData);
}
}
However, in this case tah field can't be static, since it's not possible to refer to type parameters from static members.
Generics are a compile time concept in Java. With a declaration like
public class TranslatorAndHandler<T extends DataOut>{
any variable declaration (or expression) of type TranslatorAndHandler must (really should, don't use raw types) provide a type argument to bind to the type parameter T. If you provide one, you know what type it is and you can use it. If you provide a wildcard, you don't know what type it is and therefore can't use it.
Your Wrapper class must know what type it is handling. If, at compile time, you don't know what getCorrectTAH() returns, ie. it's declared like this
public TranslatorAndHandler<?> getCorrectTAH() {...}
then you cannot expect to be able to use TranslatorAndHandler type parameter, since it is bound to ? which you cannot know at compile time.
It doesn't seem like TranslatorAndHandler should be generic in the first place. There's nothing special it can do with subtypes of DataOut.
By changing the wrapper class to the following one can dodge the compiler error:
public class Wrapper {
// Will be casted
#SuppressWarnings("unchecked")
private static final TranslatorAndHandler<DataOut> tah =
(TranslatorAndHandler<DataOut>) getCorrectTAH();
private DataOut savedData;
public Wrapper(DataIn dataIn) {
savedData = tah.translate(dataIn);
}
public void handleData() {
tah.handle(savedData); // Accepted because we can be sure that this cast will not fail
}
}
As said one can be sure that this is type-safe so this way one doesn't get a compiler error.

Categories

Resources