I haven't quite found an elegant way to solve this issue. I have an abstract class that several other classes are inheriting with an abstract method that can contain anywhere from zero to 4-5 arguments of varying types.
public abstract class Item { public abstract void use(); }
For instance, I have a Book class that inherits this and takes no arguments when overriding use(), I have a Key class that inherits and takes a String and a Queue as arguments when overriding, etc...
I've tried using generics but I have to input the number used, such as Item, when it actually depends on the class.
public abstract class Item<T,U> { public abstract void use(T arg1, U arg2); //Number of arguments/types could be more or less }
I've tried sending a variable list of Objects but the object types are always variable and I've unsure as to the syntax to receive in the inheriting classes.
public abstract class Item<T> { public abstract void use(T... arguments); } public class Book extends Item<?> { public void use(?); } public class Book extends Item<String, Queue> { //Wrong number of arguments since I can't use Item<T...> public void use(String str, Queue q); //fails }
I may just be doing something wrong - can anyone offer any assistance or insight?
Yes, we can provide parameters to abstract method but it is must to provide same type of parameters to the implemented methods we wrote in the derived classes.
Abstract classes can have instance variables (these are inherited by child classes). Interfaces can't. Finally, a concrete class can only extend one class (abstract or otherwise). However, a concrete class can implement many interfaces.
The semantics of an abstract method almost always include the parameters it should take.
Unlike concrete classes, they merely specify an interface to an object, not an object itself. All you can do with an abstract type is to declare a variable to be of that type. Such a variable can point to any actual object which is a subtype of that abstract class.
I've struggled with the same question, and there's not a perfect answer, but I can give you a few things to consider. First, you're basically trying to do something that is inherently against Object Oriented Programming, which is that you're trying to create a variable interface. The point of an interface is that code that gets an abstract version of the object (the Item rather than the Book, for example), knows how to invoke the use() method. This means that they must know what can be passed to the use() method. If the answer depends on the implementation of the abstract class or interface, then you need to ensure that the code using it actually knows what kind of implementation (Book, etc.) that it's using, otherwise it's not going to know how to invoke use() with the appropriate parameters anyway. It sounds like you need to refactor your code, in all honesty.
However, there is a way to answer your question as stated without refactoring the architecture. You could create a class that's data is all of the different types of parameters that could possibly be passed to the use() method, have the calling code set the fields of that class, and then pass that to the use() method. For example:
public class UseParameters { private String string; private Queue queue; // Any other potential parameters to use(...) public void setString(String string) { this.string = string; } public String getString() { return string; } // All of the other accessor methods, etc. }
Then, you could define the use method in Item like this:
public abstract void use(UseParameters params);
And any code using an Item would have to set the parameters of the object appropriately:
Item item = // However you're going to get the item UseParameters params = new UseParameters(); params.setString("good string"); params.setQueue(new Queue()); item.use(params);
I just want to point out that if the code above knows the Item is a Book (which is how it knows to set the String and Queue, then why not just get a Book and skip needing an abstract class with a variable use() method altogether? But I digress. Anyway, the Book would then implement the use() method like so:
@Override public void use(UseParameters params) { if(params.getString == null || params.getQueue() == null) // throw exception // Do what books do with strings and queues }
I think that gets you what you want, but you should consider refactoring, I think.
What you want is the Value Object Pattern.
Define a class that encapsulates the various parameter types into one value object, and have the abstract method accept a parameter of this type. Each variation of parameters you were considering would have its own value class.
Then simply add a generic type to the class and have the abstract method accept a parameter of that type:
public abstract class Item<V> { public abstract void use(V v); }
To use it, suppose MyItem
needs a value object of type MyValueClass
:
public class MyItem extends Item<MyValueClass> { public void use(MyValueClass v) { } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With