This question already has answers here:
How to determine an object's class?
(13 answers)
Closed 5 years ago.
Following super-class/sub-class relationship:
public abstract class A {
public Class<? extends A> getSubClass() {
Class<? extends A> clazz = ???
}
}
public class B extends A {
/* some implementation*/
}
public class Foo {
public static void main(String... args) {
A a = new B();
a.getSubClass();
}
}
Is there a way that b.getSubClass()returns the acutall sub-class type?
This
package test;
public class SubClass {
public static void main(String[] args) {
final A b = new B();
System.out.println("Class: " + b.getSubClass().getName());
}
public static abstract class A {
public Class<? extends A> getSubClass() {
return this.getClass();
}
}
public static class B extends A {
// No further implementation
}
}
produces
Class: test.SubClass$B
Even in super-class methods, the actual class is the sub-class.
Related
I need to have two methods, one that receives an extension of an abstract class (A) and another that extends this same class and extends an interface (B), like follows:
public class MyClass {
public static void main(String args[]) {
method(new C());
}
public static <T extends A & B> void method(T t) {
// some method
}
public static void method(A a) {
// some method
}
abstract static class A{
}
interface B{
}
static class C extends A{
}
static class D extends A implements B{
}
}
However I'm getting an error saying that they clash
/MyClass.java:10: error: name clash: method(A) and <T>method(T) have the same erasure
public static void method(A a) {
^
where T is a type-variable:
T extends A,B declared in method <T>method(T)
Do anyone knows a work around and what is the reason of this behavior?
Here is the following code
public abstract class A {
public abstract <E> void foo(E e);
}
the subclass:
public class B extends A {
#Override
public <OtherClass> void foo(OtherClass oc) {
oc.someOtherClassMethod(); //here compiler cannot see method
}
}
and class with main
public class C {
public static void main(String[] args) {
OtherClass oc = new OtherClass();
A a = new B();
a.foo(oc);
}
}
the error I get:
error: cannot find symbol
...
symbol: method someOtherClassMethod()
location: variable oc of type OtherClass
where OtherClass is a type-variable:
OtherClass extends Object declared in method <OtherClass>write(OtherClass )
1 error
Why cannot I use the someOtherClassMethod()? Without generics everything works, but I would like to be able to extend other classes in which I override foo() method
Solution:
public abstract class A <E> {
public abstract void foo(E e);
}
Subclass:
public class B extends A<OtherClass> {
#Override
public void foo(OtherClass oc) {
oc.someOtherClassMethod();
}
}
However, now I need use in main function:
A<OtherClass> a = new B();
try
public abstract class A<E> {
public abstract void foo(E e);
}
and
public class B extends A<OtherClass>
Having some issues with genericity.
So, here is a simple version of what I'm dealing with:
I have one abstract class and two subclasses:
public abstract class A {}
public class B extends A {}
public class C extends A {}
I'm writing writers for those classes, and I want to keep the same architecture, because all those have a lot in common. But I want to be able to call the writer without instantiating it
public abstract class AWriter<T extends A> {
public void AWritingMethod(T arg) {}
}
public class BWriter extends AWriter<B> {
public static void BWritingMethod(B arg) {
AWritingMethod(arg)
}
}
public class CWriter extends AWriter<C> {
public static void CWritingMethod(C arg) {
AWritingMethod(arg)
}
}
Obviously, I can't call AWritingMethod in BWriter and CWriter, but how could I do something like that to keep most of the work in AWriter, while still keeping BWritingMethod and CWritingMethod static ?
Thanks already !
LD
You can change your code as following, add static to AWritingMethod:
public abstract class AWriter<T extends A> {
public static void AWritingMethod(T arg) {}
}
public class BWriter extends AWriter<B> {
public static void BWritingMethod(B arg) {
AWriter.AWritingMethod(arg)
}
}
public class CWriter extends AWriter<C> {
public static void CWritingMethod(C arg) {
AWriter.AWritingMethod(arg)
}
}
or you can try using new instance of BWriter and CWriter class like this:
public abstract class AWriter<T extends A> {
public void AWritingMethod(T arg) {}
}
public class BWriter extends AWriter<B> {
public static void BWritingMethod(B arg) {
new BWriter().AWritingMethod(arg);
}
}
public class CWriter extends AWriter<C> {
public static void CWritingMethod(C arg) {
new CWriter().AWritingMethod(arg);
}
}
If you want to abstract the logic out into a separate class/method, your best bet would be to do something like this:
class AWriter {
private AWriter() {}
static <T extends A> void AWritingMethod(T arg) {}
}
public class BWriter {
private BWriter() {}
public static <T extends B> void BWritingMethod(T arg) {
AWriter.AWritingMethod(arg)
}
}
public class CWriter {
private CWriter() {}
public static <T extends C> void CWritingMethod(T arg) {
AWriter.AWritingMethod(arg)
}
}
Note that AWriter is not public, which is fine if BWriter and CWriter are in the same package, plus private constructors to prevent instantiation of static-only classes. This requires a bit more boilerplate around adding generics to each static method, but you don't have a lot of choice - you cannot use a classes generic types in a static context.
I have generic class :
public class Test<T> {
private Test<? extends T> myInstance;
public Test<? extends T> getInstance () {
return myInstance;
}
public void setInstance (Test<? extends T> argType) {
this.myInstance = argType;
}
}
And I have two classes in my class hierarchy relations:
public abstract class Alphabet {
//code here
}
and
public class A extends Alphabet{
public A() {
super();
System.out.print("This is A call");
}
}
Finally I have a class where I want to store make generic class Test with particular type and set new Instance of Object -> A through setInstance() method:
public static void main(String[] args) {
List<Alphabet> list = new ArrayList<Alphabet>();
Test<Alphabet> tAlphabet = new Test<Alphabet>();
tAlphabet.setInstance(new A()); //Here is compilation ERROR
}
But I have got the compilation error in line tAlphabet.setInstance(new A());
What is the issue with my generic class?
Your instance is a Test object as it's currently written, and you are supplying it with an Alphabet object instead. You probably want your instance to be of type Alphabet:
public class Test<T> {
private T myInstance;
public T getInstance() {
return myInstance;
}
public void setInstance(T argType) {
myInstance = argType;
}
}
This way, your Test stores an Alphabet instead of another Test.
It seems you have made things more complicated than needed. You probably want this in your Test class instead of what you actually have:
private T myInstance;
public T getInstance () {
return myInstance;
}
public void setInstance (T argType) {
this.myInstance = argType;
}
With this arrangement you would be free to setInstance(new A()) on a Test<Alphabet> instance.
I wanted to create an interface for copying an object to a destination object of the same class. The simple way is to use casting:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
#RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable {
public void copy(Copyable c);
}
public static class A implements Copyable {
private String aField = "--A--";
protected void innerCopy(Copyable c) {
A a = (A)c;
System.out.println(a.aField);
}
public void copy(Copyable c) {
innerCopy(c);
}
}
public static class B extends A {
private String bField = "--B--";
protected void innerCopy(Copyable c) {
B b = (B)c;
super.innerCopy(b);
System.out.println(b.bField);
}
}
#Test
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
But also i've found a way it can be done using generics:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
#RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable<T> {
public void copy(T t);
}
public static class A<T extends A<?>> implements Copyable<T> {
private String a = "--A--";
public void copy(T t) {
System.out.println(t.a);
}
}
public static class B<T extends B<?>> extends A<T> {
private String b = "--B--";
public void copy(T t) {
super.copy(t);
System.out.println(t.b);
}
}
#Test
#SuppressWarnings("unchecked")
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
Though the only way i've found to get rid of warnings is the annotation. And it feels like something is wrong.
So what's wrong? I can accept that something is wrong in the root of the problem. So any sort of clarification is welcome.
Your interface definition:
public interface Copyable<T extends Copyable<T>> {
void copy(T copyFrom);
}
Your implementation:
public class Example implements Copyable<Example> {
private Object data;
void copy(Example copyFrom) {
data = copyFrom.data;
}
//nontrivial stuff
}
That should take care of your warnings.
Assuming you don't want to subclass further you just need:
public static /*final*/ class AClass implements Copyable<AClass> {
For an abstract class, you do the "enum" thing:
public static abstract class AClass<T extends AClass<T>> implements Copyable<T> {
In testCopy, one of the warnings is because you're instantiating a "raw type" of Copyable rather than some concrete Copyable<T>. Once you instantiate a Copyable, it can only be applied to Ts (which include subtypes of T). In order to instantiate with a formal type, the class definitions will need to be changed slightly:
public static class A<T extends A> implements Copyable<T>
public static class B<T extends B> extends A<T>
The next issue is that a Copyable<B> can only be passed a compile-time type of B (based on the definition of Copyable). And testCopy() above is passing it a compile-time type of Copyable. Below are some examples of what will work, with brief descriptions:
public void testExamples()
{
// implementation of A that applies to A and subtypes
Copyable<A> aCopier = new A<A>();
// implementation of B that applies to B and subtypes
Copyable<B> bCopier = new B<B>();
// implementation of A that applies to B and subtypes
Copyable<B> bCopier2 = new A<B>();
}
I keep trying to figure out a way to get rid of the warnings in your first approach and I can't come up with anything that works. Even so, I think the first approach is the lesser of two evils. An unsafe cast is better than needing to give your classes such a complicated api.
A completely separate approach would be to override Object.clone() and implement Cloneable.
This is the best possible code of second approach. It compiles without any warnings.
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
#RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable<T> {
public void copy(T t);
}
public static class A<T extends A<T>> implements Copyable<T> {
private String a = "--A--";
public void copy(T t) {
System.out.println(t.a);
}
#SuppressWarnings("unchecked")
public static Copyable<Object> getInstance() {
return new A();
}
}
public static class B<T extends B<T>> extends A<T> {
private String b = "--B--";
public void copy(T t) {
super.copy(t);
System.out.println(t.b);
}
#SuppressWarnings("unchecked")
public static Copyable<Object> getInstance() {
return new B();
}
}
#Test
public void testCopy() {
Copyable<Object> b1 = B.getInstance();
Copyable<Object> b2 = B.getInstance();
Copyable<Object> a = A.getInstance();
b1.copy(b2); // this works as intended
try {
b1.copy(a); // this throws ClassCastException
fail();
} catch (ClassCastException cce) {
}
}
}
And also i figured out all that happens in this program with help of reflection:
for (Method method : A.class.getMethods()) {
if (method.getName().equals("copy")) {
System.out.println(method.toString());
}
}
for (Method method : B.class.getMethods()) {
if (method.getName().equals("copy")) {
System.out.println(method.toString());
}
}
Here is the output:
public void com.sbp.core.TestGenerics$A.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)
public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$B)
public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)
It means that:
The copy(...) methods in A and B make compiler generate "bridges" -
2 different methods for each, one with reifed argument type from
ancestor (reified T from Copyable becomes Object, reified "T extends
A" from A becomes A) and that is why it's override and not overload,
and the other one with reified argument type for defining class. First
method (with autogenerated body) downcasts its argument to call the
second (they call it a bridge). Because of this downcasting we get
ClassCastException in runtime if we call b1.copy(a).
It looks like direct type casting is cleaner and better tool for my
problem and generics are better used in their direct purpose - to
enforce compile time type checking.
I've learned Scala and now i know that the thing i wanted 2 years ago could have been achieved with contravariant type parameter and Scala's type system:
trait CopyableTo[-T] {
def copyTo(t: T)
}
class A(private var a: Int) extends CopyableTo[A] {
override def copyTo(t: A) {
println("A:copy")
t.a = this.a
}
}
class B(private var a: Int, private var b: Int) extends A(a) with CopyableTo[B] {
def copyTo(t: B) {
println("B:copy")
super.copyTo(t)
t.b = this.b
}
}
#Test
def zzz {
val b1 = new B(1, 2)
val a1 = new A(3)
val b2 = new B(4, 5)
b1.copyTo(a1)
a1.copyTo(b1)
b1.copyTo(b2)
}
Java type system is too weak for this.