I have different objects(Object A and object B). But some of the objects' fields are same. I can't change the object classes( i mean i cant write a implement/extends condition for them). I want to pass the objects to a method which uses the objects' fields They have same fields. I don't want to overloading. Which design is the most suitable for this?.
A obj1 = new A();
B obj2 = new B();
update(obj1);
update(obj2);
// my function
public <T extends myInterface> void update(T obj)
{
obj.field+=1;
}
public interface myInterface{
int field=0;
}
--------------
public class A{
int field;
.... // smt else
}
--------------
public class B{
int field;
.... // smt else
}
If you have two classes which do not implement a common interface or share a common base class, you can't really pass them to your function.
The fact that they have a common field doesn't matter.
You have 3 workarounds, none of which is really good:
Have your function accept Object type, and check its type (A or B) inside using instanceof. This is ugly and not recommended as any class can be passed inside, and also your code has to check it's type all the time.
Have your function accept Object type, Use reflection to access field with specific name, in this way:
Field field = obj.getClass().getDeclaredField('myfeild');
Object value = field.get(obj);
This is better in that your code doesn't have to check types, but more unsafe. Any class can be passed to your function, and there's some dark magic which relies on static field names. If field name changes, your code breaks.
Perhaps the best - Implement a wrapper for your objects. It will have two constructors, one for class A and one for class B. The wrapper will remember which kind of object resides inside. In its getField function if will have a single if statement. Have your function accept the wrapper type as the argument.
instanceof can be used indentify class of object. Like this:
public <T extends myInterface> void update(Object obj)
{
if ( obj instanceof A )
{
A a = (A)obj;
a.field+=1;
}
if( obj instanceof B )
{
B b = (B)obj;
b.field+=1;
}
}
Related
I am new to Java generics. I have written one function like following:
public class C<T extends MyClass> implements MyInterface<T>{
public void f(T obj){
...
obj.getName()
}
}
Above function f is called for two types of objects MySubClass1 and MySubClass2. MySubClass1 and MySubClass2 are two concreter classes inherited from abstract class MyClass and name is an attribute of MySubClass2.
When f is called with object of MySubClass2 , I would like to access name like above. I cannot figure out how to do that.
...and name is an attribute of MySubClass2
Then your method can't rely on it being there, since obj can be anything deriving from MyClass.
This suggests your design should change such that either you have separate methods or you move name to MyClass.
You could do it with an instanceof check and a cast:
if (obj instanceof MySubClass2) {
String name = ((MySubClass2)obj).getName();
}
...but nine times out of ten, using instanceof should make you step back and reconsider your design.
In a generic method that takes T constrained to MyClass only methods of MyClass are available. Since getName is implemented only in MySubClass2, you cannot access getName without a cast to MySubClass2, which goes contrary to the point of making your f() method generic in the first place.
You can pass a Function object that pulls name from T to f(), like this:
public void f(T obj, Function<T,String> getName){
...
String name = getName.apply(obj);
}
The caller would invoke f() like this:
MySubClass2 s2 = new MySubClass2();
MyInterface<MySubClass2> c = new C<>();
c.f(s2, MySubClass2::getName);
Note that this technique lets you call f on MySubClass1 objects, as long as you provide some way of getting a name:
MySubClass1 s1 = new MySubClass1();
MyInterface<MySubClass1> c = new C<>();
c.f(s1, x -> "<no-name>");
class MyClass {
private String str;
public MyClass(String str){
this.str = str;
}
public int compare(Object o) {
return str.compareTo(((MyClass)o).str); //line No.8
}
}
class Client {
public static void main(String[] args) {
MyClass m = new MyClass("abc");
MyClass n = new MyClass("bcd");
System.out.println(m.compare(n));
}
}
Why in this snippet of code the cast (MyClass)o in line number 8 is necessary, despite the fact that the Client invokes a compare method with arguments which are instances of MyClass class?
When I modify the compare method in MyClass class to form like below:
public int compare(Object o) {
System.out.println(o.getClass());
System.out.println(((MyClass)o).getClass());
return str.compareTo(((MyClass)o).str);
}
Then, the Client will produce the following result:
class MyClass
class MyClass
Thus I don't understand why the cast above is required and why I can't just do like that (without cast to MyClass):
public int compare(Object o) {
return str.compareTo(o.str);
}
Because when I do that, I get the compile time error:
str cannot be resolved or is not a field
This comes down to what the compiler knows at compile time. At compile time it knows that what is going to be passed into this method is of type Object. That means that it can guarantee the methods that are associated with the class Object, but not the methods of type MyClass.
Because that compare method takes any argument of type Object, or a subclass, you could pass anything in. What if I make a class MyOtherClass like this..
public class MyOtherClass {
public String notStr;
}
And I do something like..
MyOtherClass myOtherClass = new MyOtherClass();
MyClass myClass = new MyClass();
myClass.compare(myOtherClass);
Without the cast, you've now got a situation where at runtime, it attempts to access a field that is not there. The cast is put in place to guarantee that the object is of the correct type, or it will fail before it attempts to access that field.
Just as an Aside
I've been working extensively with a language called Groovy. It is a language that essentially sits on top of Java, but it supports things like dynamic binding and loose typing (which is what you're after here). If this kind of functionality is a must have for you, then I would recommend checking out the documentation.
o's type in compare is an Object. This means the parameter it could be a MyClass instance, but it also could not. Object doesn't have any field called str (as that one belongs to MyClass), so there's no way to get that field from it, and so the code can't compile. If you cast to MyClass, however, it will have a field called str, and so it will be able to access it.
Pre information.
public abstract class Person {}
public class A extends Person{}
public class B extends Person{}
public class C extends Person{}
public X getPersonByType(String type){
//This Method will return either A or B or C based on type
//what should X be ?
}
I need to create a method which takes in a String and returns an object which is a subtype of Person.
More Information.
Each of the classes A B C have an attribute public List roles. These cannot be moved upto the Person class as I require these uniquely named for (JPA many to many table).
Now If possible, i would not like it to return person as I would not be able to access the roles attribute (Person does not know about it). I would also prefer a solution (if possible) which does not require me to cast or use instanceOf (again if possible).
P.S tried <? extends Person> but Eclipse gave me error "return type of method is missing"
public Person getPersonByType(String type) should work fine because all your return types extend Person.
You can also add an additional class parameter if you want to avoid instanceof checks after calling this method:
public <T extends Person> T getPersonByType(String type, Class<T> type) {
...
// cast result to T
}
Using this way you would be able to assign the return type to a subclass directly:
C c = getPersonByType("c", C.class);
Be aware that this can cause ClassCastExceptions if you pass in a String and a Class parameter that don't match each other.
You should return Person X. As Person is an abstract type it can't be instantiated. You can have Person X as a reference that should point to an object of any of the concrete implementation of Person which are A B & C in your case. This is the beauty of run time polymorphism. Based on your input at the run time it would create an object of any of the A, B or C class and use reference X to point to that object.I would suggest you go through Factory Design Pattern which will give you more information about how this type of design works in real life scenario.
X should be Person.
since Person is an abstract class and can't be instantiated it will always return one of your subtypes.
If you need to know what getPersonByType returned from your calling method, you can use the instanceof operator
public Person getPersonByType(String type){
// analyze type and return appropriate instance...
if ("A instance".equals(type)) {
return new A();
}
...
}
I have several subclasses of a class "A", say B, C, and D. I store several of these in a generic
ArrayList<A> stuff;
B, C, and D have absolutely no different data members than A. The only way they differ is by different overridden methods. I would LIKE to be able to copy any instance of class A to another instance of class A (while retaining the true subclass)
Something like:
A obj1 = (A)new B(...)
A obj2 = (A)new C(...)
A obj3 = obj1.copy();
// (obj3 instanceof B) == true
Normally this would require B, C, and D to implement custom copy methods. However, this seems like a waste since the data members are exactly the same, and they would only exist so that the class is preserved.
Is there any way I could get away with only implementing copy in class A, and still preserving the underlying classes of the objects?
EDIT:
If I'm thinking correctly, the problem I would run into if just the superclass had a copy method:
class A {
int dataMember;
public A copy() {
A ret = new A(); // !!
ret.dataMember = dataMember;
return ret;
}
}
When calling "new" I couldn't generically determine what subclass of A the class is, and furthermore explicitly instantiate an instance of that. Or is there a way to do this?
You could give A a copy constructor:
class A {
public A(A other) {
//copy other's fields to this instance
}
}
Subclasses of A could also expose a constructor that took an A instance and passed it to the super constructor:
class B extends A {
public B(A other) {
super(other);
}
}
Are you familiar with clone? It will copy all your fields and mantain the derived class. Be careful that it won't do a clone of each field's object, but they will point to the same reference.
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {} // Will never happen if your object implements the Cloneable interface
}
I have two classes A and B while B is a subtype of A:
public class A {
private String stringVar;
public A() {
stringVar = "";
}
public String getStringVar() {
return stringVar;
}
public void setStringVar(String str) {
this.stringVar = str;
}
#Override
public String toString() {
return getStringVar();
}
}
Class B:
public class B extends A {
private int intVar;
public B() {
intVar = 0;
}
public int getIntVar() {
return intVar;
}
public void setIntVar(int intVar) {
this.intVar = intVar;
}
#Override
public String toString() {
return super.toString() + " " + getIntVar();
}
}
As you can see in the following main method I assign the b to a. Now "a" can't invoke b's methods which is clear, because I'm using an instance of type A now. But it behaves like a B when toString is invoked. Curious, I would have expected toString of a. Why is this so?
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
b.setIntVar(200);
b.setStringVar("foo");
a = b;
System.out.println(a);
}
}
Because a points to the implementation of B.
And is declared as A.
So behavior of B. And methods visible of A.
To use B methods do like this
((B) a).getIntVar();
Think of it like this
Object o = new FancyObject();
When compiling this only Objects methods will be accepted even though it's a FancyObjcet with lots of methods.
To use the methods of FancyObject on o do like this.
Object o = new FancyObject();
(FancyObject o).fancyMethod();
Quote "because I'm using an instance of type A now" you are still using an instance of type B. You can see it like you have upcasted b but it's the same instance.
Picture cross linked from another site with credits in the picture, if this is against the rules then somebody is free to edit this part of my answer.
This is nature of inheritance / polymorphism and overriding methods.
Overrided methods will be determined in runtime based on objects real type and not based on reference type.
Therefore a.toString() is actually b.toString() because it is determined in runtime.
http://download.oracle.com/javase/tutorial/java/IandI/override.html
The concept you need to understand is the difference between References and Objects.
a is a reference (a local variable in this case) that points first to an Object of type A and then to an Object of type B.
The compiler knows that it must be of type A (or a subtype thereof), so it can safely call all methods A defines, but they will be called on the actual Object, not on the original Type of a.
This is polymorphism: The object that a holds has static type A, but it is still an Object of dynamic type B. Dynamic dispatch therefore chooses the overridden toString() defined in B.
That's exactly how Java's runtime polymorphism works. All that matters is the actual type at runtime. What you have done is take a reference to an A and point it at an instance of B. You have changed the type of the thing that a points to.
Try
a = (A)b;
No, B Overrides the toString method of A, so if an object is an instance of B, when you call its toString method, you get whatever method that instance has. In general, if you have an object and call its methods, the method called is the one that is in the instance, not in the variable type. The only exception is static methods.
In C++, this is not the case. The method called is the one of the variable type, if one exists, unless you explicitly select the above described behavior by making a method virtual.
That is called runtime polymorphism in OOP.