Reputation: 3171
I have 2 interfaces Sub
and Obs
as illustrated below:
public interface Sub<O extends Obs<? extends Sub>>{
public void addObs(O o);
public void removeObs(O o);
public void notifyObs();
}
public interface Obs<S entends Sub<?>>{
public void update(S s);
}
Now there are 2 concrete implementations of the above as class Vie
which implements Obs
and class Mod
which implements Sub
as below:
public class Mod implements Sub<Vie<Mod>>{
private Vie<Mod>[] vies = new Vie<Mod>[0];//Here is the error.
public void addObs(Vie<Mod> vie){
vies = addToArray(vies, vie);
//Some other code;
}
public void removeObs(Vie<Mod> vie){
vies = removeFromArray(vies, vie);
//Some other code;
}
public void notifyObs(){
for(Vie<Mod> v : this.vies){
v.update(this);
}
}
}
public class Vie<M extends Mod> implements Obs<M>{
private M mod;
public void update(M){
//some code;
}
public void setMod(M mod){
this.mod.removeObs(this); //Here is the error.
mod.addObs(this); //Here is the error.
this.mod = mod;
}
}
In the above code of Mod
there is an error of initialisation of the array vies
. The correction that is applicable is :
@SuppressWarnings("unchecked")
private Vie<Mod>[] vies = (Vie<Mod>[])new Vie<Mod>[0];
And for the Vie
class's setMod
method the correction that can be applied is:
@SuppressWarnings("unchecked")
public void setMod(M mod){
this.mod.removeObs((Vie<Mod>)this);
mod.addObs((Vie<Mod>)this);
this.mod = mod;
}
As we can see that both the above cases we had to explicitly type cast the instances before they could be used by the program also we had to add @SuppressWarnings("unchecked")
so that the compiler does not throw any compile error.
Now my understanding of @SuppressWarnings("unchecked")
is that I am explicitly asking the compiler not to check the type of the instance of the variable at the compile time. If this is correct then can I run into any runtime ClassCastException
?
Also can this above code be modified such that I do not require any @SuppressWarnings("unchecked")
?
Additional Info
I have updated the code to show the utilisation of the variable vies
. This above is a basic implementation of Observer Pattern. Kindly note that this is the complete implementation as far as the Observer Pattern is concerned. What I mean to say is in actual implementation the real classes inherits other classes and interfaces whose methods are not mentioned here. But as far as vies
and mod
variable is concerned, this is complete.
Upvotes: 2
Views: 708
Reputation: 122489
The issue with creation of an array of a parameterized type has to do with the array not being able to check that elements added are the right type as arrays usually do. Since you only use the array inside the class and don't expose it to the outside, it's fine and you can just suppress the warning. The type you use and the suppression of the warning are internal implementation details of the class and outside code doesn't care.
The type mismatch of passing this
to removes()
and addObs
is a bigger issue. Vie<M>
and Vie<Mod>
are incompatible types. It's not clear why you have Vie
be generic. If you didn't make it generic, it would work:
public class Mod implements Sub<Vie> {
private Vie[] vies = new Vie[0];
public void addObs(Vie vie) {
//vies = addToArray(vies, vie);
//Some other code;
}
public void removeObs(Vie vie) {
//vies = removeFromArray(vies, vie);
//Some other code;
}
public void notifyObs() {
for (Vie v : this.vies) {
v.update(this);
}
}
}
public class Vie implements Obs<Mod> {
private Mod mod;
public void update(Mod mod) {
//some code;
}
public void setMod(Mod mod) {
this.mod.removeObs(this);
mod.addObs(this);
this.mod = mod;
}
}
If you want to be able to have this code work for Mod
and Vie
subclasses, then it would be more complicated.
Upvotes: 1
Reputation: 8841
The compiler warning you are suppressing is designed to warn about possible errors due to type erasure. Type erasure means at run-time your parameterized classes are not parameterized, but plain types. This means that the runtime knows Vie
is Vie
but it can't check what object type it holds in mod
(until you fetch it).
The general class of problems are explained here: Java GenericsFAQ. Your example is similar to the Wrapper example given.
Here is an example of the dangers of ignoring unchecked warnings using conventional classes:
ArrayList<String> as = new ArrayList<String>();
ArrayList<Integer> ai = new ArrayList<Integer>();
ArrayList ao1;
ArrayList ao2;
as.add("Hello");
ao1 = as;
ao2 = ai;
ao2.add(ao1.get(0));
Integer i = ai.get(0); // Class cast exception, even though no casting done
Your code is vaguely similar and I think it helps to understand the above example to see what you are doing. You won't see any errors in the code you have supplied as you are putting Vie<M extends Mod>
objects to a Vie<Mod>
holder. The problem lies when you come get to things out of your Vie<Mod>
holder and you assume they could be Vie<ParticularM>
objects, which is not necessarily true.
You don't have any methods at the moment fetching the Vie
objects, but if you did, at runtime the compiler would not be able to tell the exact type of the the object that Vie
was wrapping, so any casts that you make to Vie<M>
are really advice to the compiler and cannot actually be enforced.
In your code, it is all held together by the Vie class itself which moderates access to the Mod class and ensures (I think) that you are always putting consistent objects in the array.
But your code is not complete and you are only a step away from adding some piece of code that allows a Vie<M2 extends Mod>
object to be put in an array where you think are Vie<M1 extends Mod>
objects.
So, in summary, I think the current code is safe as it stands in isolation, but any changes could introduce errors that the compiler could not detect.
Upvotes: 0