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.
Related
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());
}
}
public class BinaryVertex {
public BinaryVertex parent,left,right;
}
public class BSTVertex extends BinaryVertex {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
I'm making a tree / graph api for school, approaching it from a oop standpoint. But im trying to figure out a way for the inherited class to treat some of its base class variables as its own type (i.e. parent,left,right should be treated as BSTVertex when called from BSTVertex but treated as BinaryVertex when called from BinaryVertex) without casting.
I'm thinking of generics but I'm not sure how to implement that in this situation.
UPDATE
Nice, didnt know you could use extend in generics. But I'm getting a BSTVertex<T> cannot be converted to T error with this:
public class Test {
public static void main(String[] args) {
new AVLVertex();
BSTVertex<BSTVertex> v = new BSTVertex<BSTVertex>();
v.foo();
}
class BinaryVertex<T extends BinaryVertex> {
public T parent, left, right;
}
class BSTVertex<T extends BSTVertex> extends BinaryVertex<T> {
public T foo() {
return this; //error here
}
}
class AVLVertex extends BSTVertex<AVLVertex> {
// this might probably end up being abstract too
}
foo needs to return a vertex of the same type as caller, i.e. if AVLVertex calls foo its expecting to get AVLVertex not BSTVertex
Yes, you can use generics like this:
public class BinaryVertex<T extends BinaryVertex<T>> {
public T parent, left, right;
}
public class BSTVertex extends BinaryVertex<BSTVertex> {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
The same way the Comparable interface implemented, so subclasses receive the same type to compareTo method. For example, Integer implements Comparable<Integer>, so its compareTo method receives Integer argument.
Also please note the it would be better to create your own random number generator like this:
public class BSTVertex extends BinaryVertex<BSTVertex> {
private static final Random r = new Random();
public void foo() {
left = new BSTVertex();
if(r.nextBoolean()) left.foo();
}
}
UPDATE
In your updated code (in future please ask new question instead) you cannot safely cast, because you can potentially write later:
class RBVertex extends BSTVertex<RBVertex>{}
class AVLVertex extends BSTVertex<RBVertex>{}
This is ok from the compiler's point of view, but your AVLVertex generic argument is actually not an AVLVertex. That's why you have a compilation error in foo() method: your class can be later possibly extended in the way that would make your T incompatible with this.
You can fix this problem by doing an unchecked cast:
#SuppressWarnings("unchecked")
public T foo() {
return (T) this;
}
In this way if you mistakenly create class AVLVertex extends BSTVertex<RBVertex>{}, it will still compile, but upon calling AVLVertex.foo() you may have a runtime ClassCastException.
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
}
I'm struggling with this aspect of Generics in Java. Hopefully someone can help me see the ways.
I have a class that holds a List of objects. This code works, but I want to get rid of the cast. How can I make this more generic?
public class Executor {
List<BaseRequest<BaseObj>> mRequests = new ArrayList<BaseRequest<BaseObj>>();
public Executor() {
}
#SuppressWarnings("unchecked")
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add((BaseRequest<BaseObj>) request);
}
public void execute() {
for (BaseRequest<BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
In the posted snippet you need the cast because BaseRequest<? extends BaseObj> is not a subtype of BaseRequest<BaseObj>, and the cast can't be checked at runtime because of type erasure, and that's why the compiler warns you. But if you change the declaration of mRequests:
public class Executor {
List<BaseRequest<? extends BaseObj>> mRequests = new ArrayList<>();
public Executor() {
}
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add(request);
}
public void execute() {
for (BaseRequest<? extends BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
class BaseRequest<T> {}
class BaseObj {}
Let's resolve the problem step-by-step. You want to be able to call
req.add(new BaseRequest<ExtObj1>());
req.add(new BaseRequest<ExtObj2>());
req.add(new BaseRequest<ExtObj3>());
where ExtObj[1|2|3] extends BaseObj. Given the List interface:
List<T> {
void add(T el);
}
we need to find a common supertype for BaseRequest<ExtObj1>, BaseRequest<ExtObj2> and BaseRequest<ExtObj3>. One supertype is BaseRequest<?> and another one is BaseRequest<? extends BaseObj>. I picked the second one because it's the most restrictive possible. You should know that in Java BaseRequest<ExtObj1> is not a subtype of BaseRequest<BaseObj> because generics are invariant.
Now that we have the right declaration for mRequests, finding the API for Executor.add() is straightforward. BTW, if the method body you need is really that simple, you don't even need the type parameter:
public void add(BaseRequest<? extends BaseObj> request) {
mRequests.add(request);
}
Warnings are not errors. Warnings are there so you check if you have an error because it may not be checked automatically. You should check it and then use the annotation to note that warning was already checked.
In your case it warns BaseRequest<T> is not equivalent to BaseRequest<BaseObj>.
Example:
public class NumberWrapper<N extends Number> {
private N value;
public void setValue(N value) {
this.value = value;
}
}
public class MainClazz {
private NumberWrapper<Integer> wrappedNumber = new NumberWrapper<Integer>();
public void run() {
Number n = externalSource.getNumber();
wrappedNumber.setValue(n); // <-- Error. What if getNumber returns a double?
}
}
You can have this error ir not depending on how you complete/integrate the code you are showing.
I am having some trouble grasping generics. I've read through Oracle's tutorial on generics and it doesn't seem to address my question. I also don't know what to search for in finding my answer.
Let's say that I have the following code:
public abstract class Buff<V> {
public V value;
{
public interface Buffable<V> {
public void buff(Buff<V extends Buff> buff);
}
public class DoubleBuff extends Buff<double> {
public double value;
}
public class DoubleBuffable implements Buffable<DoubleBuff> {
public void Buff(DoubleBuff buff) {
//implementation
}
}
I want to be able to create classes that inherit Buff and have the member "value," but specify value's type (See DoubleBuff). I also want to define classes that implement the buff method using an input parameter that is of a subtype of Buff.
DoubleBuffable is some class that implements Buffable, but expects an input of DoubleBuff and not StringBuff.
Am I expressing my generics correctly?
Firstly, syntax. The declaration:
public interface Buffable<V> {
public void buff(Buff<V extends Buff> buff);
}
Should be:
public interface Buffable<V extends Buff> {
public void buff(Buff<V> buff);
}
The type variable you want, should be specified in the class declaration.
But you say:
I also want to define classes that implement the buff method using an input parameter that is of a subtype of Buff.
This way, the below declaration would suit your statement better:
public interface Buffable<V extends Buff<?>> {
public void buff(V buff);
}
You may want to change that <?> part if you need a more specific type of Buff.
Lastly, other required change and the final classes:
public abstract class Buff<V> {
public V value;
}
public interface Buffable<V extends Buff<?>> {
public void buff(V buff);
}
// instead of primitive types, you should use their wrappers: double-->Double
public class DoubleBuff extends Buff<Double> {
public double value;
}
public class DoubleBuffable implements Buffable<DoubleBuff> {
public void buff(DoubleBuff buff) {
//implementation
}
}
Primitives can't be used as generic types. Try "Double" instead of "double".