I have two classes which I want to generalize:
public class General<T> {
T content;
setContent(T content) { ... };
}
public class Specific {
Foo text;
setText(Foo text) { ... };
}
T can be three different types, all of them classes. Foo is one of those, Pair is another.
I want to introduce an abstract superclass to generalize access to the attributes:
public abstract class Superclass {
abstract void setText(Foo text);
}
Both classes (General and Specific) extend this superclass. But now I have a problem implementing the setter of the class General:
public void setText(Foo text) {
if (T is of type Foo) content = (T) text;
if (T is of type Pair) ((Pair)content).setHead(text);
...
}
What do I have to write instead of is of type? Is there any such expression in Java which gets evaluated at compile time?
I can check what type of content is, but I guess this won't work if that is null. Also, I could replace T by something like ? extends Object, but how would I create an instance of (formerly) T then?
I googled but I couldn't find a sufficient answer to this specific problem.
You can create 2 different methods for these classes:
public abstract class Superclass {
abstract void setText(Foo text);
abstract void setText(Pair text);
}
Instead of having an abstract class use the concept of interfaces(with parent reference) then u can decide dynamically....
Related
I'm new to Java.
There is repeating code in multiple files in a project I'm working on.
Object types can be Thing1, Thing2, Thing3 etc.
So the code looks like:
if (Thing1.getStuff() instanceof String) {
myObj.setString("Hello");
} else {
myObj.setString("World");
}
I want to extend myObj with a class and method to handle this, as such:
public class myObj extends DoStuff {...}
--
class DoStuff {
public String doThis(*WHAT_TYPE_TO_USE* input) {
var String = input.myMethod(); // I need to call method.
return "String after some logic";
}
}
Which should allow me to run:
myObj.doThis("Something");
However, I can't specify input to be a specific type in the method as it could be Thing1, Thing2 etc. Also, Thing1 and Thing2 can't be dynamically imported, can they? How can I run myMethod (which exists in Thing1 and Thing2)?
Any advice is appreciated.
You need your Thing classes to implement a common interface such as
public interface Thing {
public String myMethod();
}
public class Thing1 implements Thing {
...
}
If they don't have a common supertype, then the two myMethod methods are unrelated. The fact that they have the same name is irrelevant to Java; they're distinct methods defined in distinct classes. You can access them with reflection shenanigans, but then you're giving up a lot of type safety (at that point, you would just take an Object and trust the user to provide a value of the correct type; it's ugly and messy and I don't recommend it).
If the classes in question are third-party classes (i.e. that you don't control) and don't implement a common interface, then you need the adapter pattern. Basically, you define a new class that does implement the interface and wraps an instance of the original.
public class Thing1Adapter implements Thing {
private Thing1 impl;
public Thing1Adapter(Thing1 impl) {
this.impl = impl;
}
#Override
public String myMethod() {
return this.impl.myMethod();
}
}
...
DoThis(new Thing1Adapter(myThing1));
I have the following generic interface:
public interface I<T> {
void method(T key);
}
which it is implemented by two different classes (A y B).
public class A implements I<Integer> {
#Override
void method(Integer key) {
//do smth
}
public class B implements I<String> {
#Override
void method(String key) {
//do smth
}
Futhermore, there is a Java class MyClass where a new instance of A or B is created depending on the T param.
public class MyClass<T> {
public void f() {
I<T> object = //here is the problem
}
}
My question is the following:
Is it possible to achieve it without passing the object of T class?
Pass a Supplier.
class MyClass<T> {
public void f(Supplier<I<T>> supplier) {
I<T> object = supplier.get();
}
}
new MyClass<String>().f(B::new);
new MyClass<Integer>().f(A::new);
no. you have to have something concrete to disambiguate the instantiation. remember, at runtime the generic bindings are gone (they are only syntactic sugar). if you doubt this, compile the same code with and without the generic hints. the output classes will be bytewise identical.
you basically have to have "some concrete reference to a type", either as presented by Igor above, or something else (Class.forName( "ClassName" ), ClassName.class, etc.) or dynamically build a class via java.lang.reflect.Proxy.
Igor's example just creates an anonymous factory as a lambda, but in the end, he's still passing the reference to a class, wrapped in a factory method, and bound as a lambda.
now something you "could" do, if you want to pass the Class, you could change your binding to or something similar, and pass a Class reference to use for instantiation. then you can do something like
_pass_in_ref.newInstance();
_pass_in_ref::new
etc.
caveat emptory
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?
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 the types to be used as argument are always variable I don't see a reason to use generics. Just use plain Object type:
public abstract class Item {
public abstract void use(Object ... arguments);
}
public class Book extends Item {
public void use(Object ... arguments) { ... }
}
The best approach I can think of is to group the items according to the behavior of their use() method.
Example
public abstract class QueueableItem {
public abstract void use(String, Queue);
}
public abstract class OrdinaryItem{
public abstract void use(String);
}
If the grouped items share a common behavior (common as in same method signature & return value), you can define and extend a parent class that will contain the definition of this common behavior.
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.
In Java I have two classes:
Class A
{
public String ID;
public Object Name;
}
Class B
{
public String ID;
public Object Name;
}
I want to have a method where I can pass it either a Class A or B object:
public void SomeMethod(??? arg)
{
String id = arg.ID;
Object name= arg.Name;
}
Is it possible to pass an object of either class A or B to this method? If so, how is the method's signature written?
The only solution I can think of is to create an interface that both Class A and B implements containing get and set methods to set the fields ID and Name. Then the method's signature would be a parameter whose type is the interface. I was hoping that maybe there is a simpler way, possibly with generics?
You are correct with needing to use an interface (or an abstract class) with the appropriate method signatures. To java the two class are different with nothing (beside Object) in common. You need to create a class hierarchy refelecting the commonality between them.
Use method overloading.
public void SomeMethod(A arg)
{
String id = arg.ID;
Object name= arg.Name;
}
public void SomeMethod(B arg)
{
String id = arg.ID;
Object name= arg.Name;
}
You could make an interface and have A and B implement it. It really depends on your application. For small programs, I would just stick with method overloading since it just introduces unnecessary abstraction into your program.
For larger applications where extensibility is a priority, you may want to consider using an interface. Suppose later on you want to write classes C and D which also have SomeMethod(). Using an interface makes it so that you don't have to go through your entire code and overload appropriate methods over and over again.
If you know for sure that A and B are the end of the story, then there's no need to make an interface.
EDIT: If there's a lot of code to be duplicated, then make a helper method:
public void SomeMethod(A arg)
{
HelpMePlease( arg.ID, arg.Name );
}
public void SomeMethod(B arg)
{
HelpMePlease( arg.ID, arg.Name );
}
private void HelpMePlease( String id, Object name ) {
// 1000 lines of code here
}
You don't need generic types. Simple inheritance will do the job
abstract class Base {
public String ID;
public Object Name;
}
class A extends Base {
}
class B extends Base {
}
public void SomeMethod(Base arg)
{
String id = arg.ID;
Object name= arg.Name;
}
Generics are intended to improve type safety during compilation.
What you are asking about seems to be something akin to C++ concepts or various other languages' duck typing.
In Java, if some sequence of operations need to be performed on two disparate types, you need to introduce an interface or resort to scripting/reflection.
Define two interfaces, hasID and hasName, and then:
public class MyClass<A extends hasID & hasName>{
public void SomeMethod(A object) {
String id = object.getID();
Object name= object.getName();
}
}
Where getID and getName are defined on their respctive interfaces.
Why can't Java classes have abstract fields like they can with abstract methods?
For example: I have two classes that extend the same abstract base class. These two classes each have a method that is identical except for a String constant, which happens to be an error message, within them. If fields could be abstract, I could make this constant abstract and pull the method up into the base class. Instead, I have to create an abstract method, called getErrMsg() in this case, that returns the String, override this method in the two derived classes, and then I can pull up the method (which now calls the abstract method).
Why couldn't I just make the field abstract to begin with? Could Java have been designed to allow this?
You can do what you described by having a final field in your abstract class that is initialised in its constructor (untested code):
abstract class Base {
final String errMsg;
Base(String msg) {
errMsg = msg;
}
abstract String doSomething();
}
class Sub extends Base {
Sub() {
super("Sub message");
}
String doSomething() {
return errMsg + " from something";
}
}
If your child class "forgets" to initialise the final through the super constructor the compiler will give a warning an error, just like when an abstract method is not implemented.
I see no point in that. You can move the function to the abstract class and just override some protected field. I don't know if this works with constants but the effect is the same:
public abstract class Abstract {
protected String errorMsg = "";
public String getErrMsg() {
return this.errorMsg;
}
}
public class Foo extends Abstract {
public Foo() {
this.errorMsg = "Foo";
}
}
public class Bar extends Abstract {
public Bar() {
this.errorMsg = "Bar";
}
}
So your point is that you want to enforce the implementation/overriding/whatever of errorMsg in the subclasses? I thought you just wanted to have the method in the base class and didn't know how to deal with the field then.
Obviously it could have been designed to allow this, but under the covers it'd still have to do dynamic dispatch, and hence a method call. Java's design (at least in the early days) was, to some extent, an attempt to be minimalist. That is, the designers tried to avoid adding new features if they could be easily simulated by other features already in the language.
Reading your title, I thought you were referring to abstract instance members; and I couldn't see much use for them. But abstract static members is another matter entirely.
I have often wished that I could declare a method like the following in Java:
public abstract class MyClass {
public static abstract MyClass createInstance();
// more stuff...
}
Basically, I would like to insist that concrete implementations of my parent class provide a static factory method with a specific signature. This would allow me to get a reference to a concrete class with Class.forName() and be certain that I could construct one in a convention of my choosing.
Another option is to define the field as a public (final, if you like) in the base class, and then initialize that field in the constructor of the base class, depending upon which subclass is currently being used. It's a bit shady, in that it introduces a circular dependency. But, at least it's not a dependency that can ever change -- i.e., the subclass will either exist or not exist, but the subclass's methods or fields can not influence the value of field.
public abstract class Base {
public final int field;
public Base() {
if (this instanceof SubClassOne) {
field = 1;
} else if (this instanceof SubClassTwo) {
field = 2;
} else {
// assertion, thrown exception, set to -1, whatever you want to do
// to trigger an error
field = -1;
}
}
}