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.
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.
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.
Related
Ok, while I tried to find a title that explains the problem I probably have to expand on it.
Recently I implemented a small program that will be used to control a tape library. Knowing it had to work with multiple different types of tape library so the following design was developed.
interface Tapelibrary<T extends TapeDrive> {
List<T> getListofDrives();
void doSomethingWithDrive(T d);
}
class SpecificTapeLibrary implements Tapelibrary<HPDrive> {
private List<HPDrive> driveList;
SpecificTapeLibrary() {
driveList.add(new HPDrive());
driveList.add(new HPDrive());
driveList.add(new HPDrive());
}
#Override
public List<HPDrive> getListofDrives() {
return driveList;
}
#Override
public void doSomethingWithDrive(HPDrive d) {
d.doSomethingHPspecific();
}
}
abstract class TapeDrive {
void doSomething() {
}
}
class HPDrive extends TapeDrive {
void doSomethingHPspecific() {
}
}
The correct tape library is determined by a factory based on command line arguments.
public static void main(String[] args) {
Tapelibrary<? extends TapeDrive> t = new TapeLibraryFabric().get();
List<? extends TapeDrive> listOfDrives = t.getListofDrives();
// the user selects a drive by using a small UI or something
TapeDrive selectedDrive = listOfDrives.get(0);
t.doSomethingWithDrive(selectedDrive); // compiler error
}
This does make sense since the compiler would have to explicitly cast the supertype TapeDrive to the subtype HPDrive which is expected by the doSomethingWithDrive(HPDrive) methods in SpecificTapeLibrary
How would this be solved in a good oop way? I ended up not using generics and casting inside the doSomethingWithDrive method (as suggested here:How to Pass a Child Class into a method requiring Super Class as parameter). But that can't be the optimal solution.
While writing this post another solution popped into my head which is much cleaner. The DriveSelector class encapsulates the selection process.
class DriveSelector {
<T> T selectDrive(List<T> inputList) {
// give the user an UI or something to select a drive
return inputList.get(0);
}
}
// the tape library then uses the selector
public void doSomethingWithSelectedDrive(DriveSelector selector) {
HPDrive t = selector.selectDrive(driveList);
t.doSomethingHPspecific();
}
Any other ideas?
Do all of your work in a generic method:
static <T extends TapeDrive> void doStuff(Tapelibrary<T> t) {
List<T> listOfDrives = t.getListofDrives();
// the user selects a drive by using a small UI or something
T selectedDrive = listOfDrives.get(0);
t.doSomethingWithDrive(selectedDrive);
}
Then call this from your main method:
Tapelibrary<? extends TapeDrive> t = new TapeLibraryFabric().get();
doStuff(t);
Ideone demo
The way this works is that it removes all of the wildcards - the thing about wildcards is that the compiler treats every one as different, even if the values are derived from a single generic instance. By putting things into the generic method like this, you allow the compiler to know that all of the Ts are the same type - thus it can know that the calls are safe.
Let's say I have a manufacturing scheduling system, which is made up of four parts:
There are factories that can manufacture a certain type of product and know if they are busy:
interface Factory<ProductType> {
void buildProduct(ProductType product);
boolean isBusy();
}
There is a set of different products, which (among other things) know in which factory they are built:
interface Product<ActualProductType extends Product<ActualProductType>> {
Factory<ActualProductType> getFactory();
}
Then there is an ordering system that can generate requests for products to be built:
interface OrderSystem {
Product<?> getNextProduct();
}
Finally, there's a dispatcher that grabs the orders and maintains a work-queue for each factory:
class Dispatcher {
Map<Factory<?>, Queue<Product<?>>> workQueues
= new HashMap<Factory<?>, Queue<Product<?>>>();
public void addNextOrder(OrderSystem orderSystem) {
Product<?> nextProduct = orderSystem.getNextProduct();
workQueues.get(nextProduct.getFactory()).add(nextProduct);
}
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy())
factory.buildProduct(workQueues.get(factory).poll());
}
}
Disclaimer: This code is merely an example and has several bugs (check if factory exists as a key in workQueues missing, ...) and is highly non-optimal (could iterate over entryset instead of keyset, ...)
Now the question:
The last line in the Dispatcher (factory.buildProduct(workqueues.get(factory).poll());) throws this compile-error:
The method buildProduct(capture#5-of ?) in the type Factory<capture#5-of ?> is not applicable for the arguments (Product<capture#7-of ?>)
I've been racking my brain over how to fix this in a type-safe way, but my Generics-skills have failed me here...
Changing it to the following, for example, doesn't help either:
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy()) {
Product<?> product = workQueues.get(factory).poll();
product.getFactory().buildProduct(product);
}
}
Even though in this case it should be clear that this is ok...
I guess I could add a "buildMe()" function to every Product that calls factory.buildProduct(this), but I have a hard time believing that this should be my most elegant solution.
Any ideas?
EDIT:
A quick example for an implementation of Product and Factory:
class Widget implements Product<Widget> {
public String color;
#Override
public Factory<Widget> getFactory() {
return WidgetFactory.INSTANCE;
}
}
class WidgetFactory implements Factory<Widget> {
static final INSTANCE = new WidgetFactory();
#Override
public void buildProduct(Widget product) {
// Build the widget of the given color (product.color)
}
#Override
public boolean isBusy() {
return false; // It's really quick to make this widget
}
}
Your code is weird.
Your problem is that you are passing A Product<?> to a method which expects a ProductType which is actually T.
Also I have no idea what Product is as you don't mention its definition in the OP.
You need to pass a Product<?> to work. I don't know where you will get it as I can not understand what you are trying to do with your code
Map<Factory<?>, Queue<Product<?>>> workQueues = new HashMap<Factory<?>, Queue<Product<?>>>();
// factory has the type "Factory of ?"
for (Factory<?> factory: workqueues.keySet())
// the queue is of type "Queue of Product of ?"
Queue<Product<?>> q = workqueues.get(factory);
// thus you put a "Product of ?" into a method that expects a "?"
// the compiler can't do anything with that.
factory.buildProduct(q.poll());
}
Got it! Thanks to meriton who answered this version of the question:
How to replace run-time instanceof check with compile-time generics validation
I need to baby-step the compiler through the product.getFactory().buildProduct(product)-part by doing this in a separate generic function. Here are the changes that I needed to make to the code to get it to work (what a mess):
Be more specific about the OrderSystem:
interface OrderSystem {
<ProductType extends Product<ProductType>> ProductType getNextProduct();
}
Define my own, more strongly typed queue to hold the products:
#SuppressWarnings("serial")
class MyQueue<T extends Product<T>> extends LinkedList<T> {};
And finally, changing the Dispatcher to this beast:
class Dispatcher {
Map<Factory<?>, MyQueue<?>> workQueues = new HashMap<Factory<?>, MyQueue<?>>();
#SuppressWarnings("unchecked")
public <ProductType extends Product<ProductType>> void addNextOrder(OrderSystem orderSystem) {
ProductType nextProduct = orderSystem.getNextProduct();
MyQueue<ProductType> myQueue = (MyQueue<ProductType>) workQueues.get(nextProduct.getFactory());
myQueue.add(nextProduct);
}
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy())
buildProduct(workQueues.get(factory).poll());
}
public <ProductType extends Product<ProductType>> void buildProduct(ProductType product) {
product.getFactory().buildProduct(product);
}
}
Notice all the generic functions, especially the last one. Also notice, that I can NOT inline this function back into my for loop as I did in the original question.
Also note, that the #SuppressWarnings("unchecked") annotation on the addNextOrder() function is needed for the typecast of the queue, not some Product object. Since I only call "add" on this queue, which, after compilation and type-erasure, stores all elements simply as objects, this should not result in any run-time casting exceptions, ever. (Please do correct me if this is wrong!)
My problem is as follows:
We have an Algorithm that works internally with
Expression-objects that have a "String getContent()" method
Manipulator-objects that manipulate on Expressions using the "Expression manipulate(Expression e)" method
This will become a framework in Java.
To solve a real problem, one needs to give a specific implementation
of both an Expression and a Manipulator and the Algorithm class will do the rest.
Let's say we need a ProblemExpression and a ProblemManipulator
for a specific problem.
The ProblemExpression may contain a lot of new fields,
which can be used by the ProblemManipulator.
Right now, I can only think of two ways to write clean code:
Let ProblemManipulator.manipulate assume its arguments are ProblemExpressions
Use instanceOf
But I've got the feeling this is not how I should do it.
Any other suggestions?
Regards and thank you in advance,
Xaero.
Sounds like you should use a Generic. Like
interface Manipulator<E extends Expression> {
public void manipulate(E expression);
}
class ProblemManipulator implements Manipulator<ProblemExpression> {
public void manipulate(ProblemExpression expression) {
// expression is a ProblemExpression
}
}
As "Problem" is a different problem, it can be an interface that extends Expression like so:
interface IProblemExpr extends Expression
{ //additional methods
}
class ProblemExpression implements IProbExpr
{
}
class ProblemManipulator()
{
ProblemManipulator(IProblemExpr expr)
{
..
}
}
Generics are not enough, if both ProblemExpresions and ProblemManipulators can be accessed publicly.
At first i thought some kind of factory framework would do the trick.
I.e., either Expressions need to be able to create Manipulators or vice-versa.
for example, say ProblemManipulators were private inner classes of ProblemExpressions - obtained from Expression#createManipulator(...).
However, this does not quite do the trick ... in the end, if the Algorithm is allowed to 'hold onto references to' both the Expression and Manipulator, and can obtain different unrelated implementations, then the Algorithm implementation can always (if incorrectly written) wind up invoking the wrong Manipulator for a given Expression - nothing can be done at compile time to prevent this runtime mistake as all Manipulators can be invoked with any Expression.
So, it seems to me that invocation of the Manipulator (or Expression) must 'go thru' the Expression (or conversely the Manipulator) thus ensuring that the correct Manipulator is invoked for the given Expression.
I.e., Expression needs 'manipulate()' method which delegates to the appropriate Manipulator.
I studied the way generics work, and I came up with the following solution:
First, I created a two classes, one for the expression and one for the manipulator:
public class ObjectExpression { }
public class ObjectManipulator <E extends ObjectExpression> {
public void calculate(Set<E> objects) {
... // Do something
}
}
Next, I created an Algorithm class, which is generic.
Two classes are needed:
Some expression
Something that manipulates this type of object
We get:
public class Algorithm <F extends ObjectExpression, E extends ObjectManipulator<F>> {
E om;
public Algorithm( E om ) {
this.om = om;
}
public void run(Set<F> objects) {
om.calculate(objects);
}
}
Then, I created an implementation for the String case:
we need an expression and a manipulator
public class StringExpression extends ObjectExpression {
}
public class StringManipulator extends ObjectManipulator<StringExpression> {
#Override
public void calculate(Set<StringExpression> objects) {
// Do String stuff
}
}
Then, we can run the Algorithm as follows for Objects:
Algorithm<ObjectExpression, ObjectManipulator<ObjectExpression>> algo1 = new Algorithm<ObjectExpression, ObjectManipulator<ObjectExpression>>(manipo);
Set<ObjectExpression> objects = new HashSet<ObjectExpression>();
... // fill set
algo1.run(objects);
And for Strings:
StringManipulator manips = new StringManipulator();
Algorithm<StringExpression, StringManipulator> algo2 = new Algorithm<StringExpression, StringManipulator>(manips);
Set<StringExpression> strings = new HashSet<StringExpression>();
... // fill set
algo2.run(strings);
To me, this seems an elegant solution.
What do you think?
Any alternatives/improvements?
Alright, I thought I understood generics pretty well, but for some reason I can't get my head wrapped around why this doesn't work. I have two classes, or I should say that Google has two classes (I'm trying to implement their Contacts API). They have a ContactEntry class (abbreviated below):
package com.google.gdata.data.contacts;
public class ContactEntry extends BasePersonEntry<ContactEntry> {
public ContactEntry() {
super();
getCategories().add(CATEGORY);
}
public ContactEntry(BaseEntry<?> sourceEntry) {
super(sourceEntry);
}
}
I left off one or two methods, but nothing important, its really just an implementation of its parent class BasePersonEntry which has most of the important stuff that concerns a person entry abbreviated code below:
package com.google.gdata.data.contacts;
public abstract class BasePersonEntry<E extends BasePersonEntry> extends
BaseEntry<E> {
public BasePersonEntry() {
super();
}
public BasePersonEntry(BaseEntry<?> sourceEntry) {
super(sourceEntry);
}
public List<CalendarLink> getCalendarLinks() {
return getRepeatingExtension(CalendarLink.class);
}
public void addCalendarLink(CalendarLink calendarLink) {
getCalendarLinks().add(calendarLink);
}
public boolean hasCalendarLinks() {
return hasRepeatingExtension(CalendarLink.class);
}
}
Now... what I can't quite understand is if I do something like the following:
public void method1(StringBuilder sb, ContactEntry contact) {
if (contact.hasCalendarLinks()) {
for (CalendarLink calendarLink : contact.getCalendarLinks()) {
...
}
}
}
Everything works fine. It is able to interpret that getCalendarLinks returns a list of type CalendarLink. However, if I want to abstract this method and have my method use BasePersonEntry, like the following:
public void method1(StringBuilder sb, BasePersonEntry entry) {
if (entry.hasCalendarLinks()) {
for (CalendarLink calendarLink : entry.getCalendarLinks()) {
...
}
}
}
I get a compiler error:
incompatible types
found : java.lang.Object
required: com.google.gdata.data.contacts.CalendarLink
For the life of me I just can't understand why? The call to getCalendarLinks is the EXACT same method (via inheritance), its returning the EXACT same thing. Maybe it has to do with BasePersonEntry being an abstract class?
If anyone, can shed some light on this I would be very much obliged. If it helps you can find a full version of this source code hosted by Google here: Link To Google Library Download. I was attempting this with version 1.41.3 of their gdata-java libraries.
The problem with your new definition, is that it's using Raw type not Generic type.
As a result type is erased from everything, including getCalendarLinks and its signature is reduced to equivalent of List<Object> getCalendarLinks( )
To fix it, change declaration to:
public void method1(StringBuilder sb, BasePersonEntry<?> entry)
Note <?> after BasePersonEntry. This way it's generic type.
Also, you probably want to change the class generic declaration to
public abstract class BasePersonEntry<E extends BasePersonEntry<E> >
Without <E> your compiler ( or IDE ) will generate an unchecked warning.
So, I have some code that looks approximately like (truncated for brevity - ignore things like the public member variables):
public class GenericThingy<T> {
private T mValue;
public final T[] mCandidates;
public GenericThingy(T[] pCandidates, T pInitValue) {
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(T pNewValue) {
mValue = pNewValue;
}
}
public class GenericThingyWidget {
private final GenericThingy<?> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<?> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue(mBox.getSelectedItem());
}
}
}
My problem is that the mThingy.setValue(mBox.getSelectedItem()); call generates the following error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (Object)
I can get around this by removing the <?> from the declaration of mThingy and pThingy in GenericThingyWidget - which gives me a "GenericThingy is a raw type. References to GenericThingy should be parameterized" warning.
I also tried replacing the setValue call with
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
which I genuinely expected to work, but that produced a very similar error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
Is there any way to do this without generating "raw type" warnings ("unchecked cast" warnings I'm OK with) and without making GenericThingyWidget into a generic type? I'd think I could cast the return of mBox.getSelectedItem() to something, but I can't figure out what that would be.
As a bonus question, why does the replacement call to mThingy.setValue not work?
You lack information in GenericThingyWidget.
The ? you put means : any class extending object. Which means any, not some particular one but I don't know which one. Java can't relate one ? to another, they can not be related one to the other in a class hierarchy tree. So
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
this tries to put an object of any class in the setValue, which is waiting for any other class, but the ? can not tell Java these two any should be the same class.
Without parameterizing GenericThingyWidget, I don't see any way to work around it.
What I would do : parameterize GenericThingyWidget, and create a Factory static parameterized method :
public static <T> GenericThingyWidget<T> make(T someObject){
...
}
I see two possibilities.
With a private addition to GenericThingyWidget— Goetz's capture helper pattern:
public void applySelectedValue() {
helper(mThingy, mBox.getSelectedIndex());
}
private static <T> void helper(GenericThingy<T> pThingy, int pIndex) {
pThingy.setValue(pThingy.mCandidates[pIndex]);
}
Or, quick-and-dirty, with a modification to the API of GenericThingy:
public void setValue(int value) {
mValue = mCandidates[value];
}
As a bonus question, why does the replacement call to mThingy.setValue not work?
The article by Brian Goetz probably explains this better than I will, but I'll give it a try.
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
The compiler knows that mThingy has some type parameter, but it doesn't know what the that type is, because it is a wildcard. It creates a placeholder for this type—"capture#4-of ?". The compiler also knows that mCandidates has some type, but it doesn't know what it is either. It creates brand new "capture" type—"capture#5-of ?" While you and I can reason that these should be the same type, the compiler (at least for now) can't jump to that conclusion. Thus, you get the error message.
The capture helper gets around that. Although the compiler doesn't know what the type is, it knows it has a type, so it allows you to pass it to the helper method. Once inside the helper method, there are no wildcards, and the compiler doesn't have to do any reasoning about whether the wildcards really refer to the same type.
Update
OK, try this:
public class GenericThingy<T> {
private Class<T> mClazz;
private T mValue;
public final T[] mCandidates;
public GenericThingy(Class<T> clazz, T[] pCandidates, T pInitValue) {
mClazz = clazz;
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(Object newValue) throws ClassCastException {
mValue = mClazz.cast(newValue);
}
}
What you need to to is parameterize GenericThingyWidget like so:
public class GenericThingyWidget<T> {
private final GenericThingy<? super T> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<? super T> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue((T) mBox.getSelectedItem());
}
}
}
Technically, you don't need the ? super T for your example, and would be fine with just a T, and perhaps it would be better in real code if you ever want to get from the GenericThingy instead of just inserting into it.
As KLE said, You can just de-parameterize GenericThingy (replace all the T's with objects). In fact, I think you have to unless you plan to pass the class of T to the constructor of GenericThingyWidget, and then dynamically cast from your mbox.getSelectedItem(), since as far as I can tell, getSelectedItem() only returns Object.