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.
Related
Trying to add a base interface with method so all derived classes have to implement the method or use default method. What's the best way to going about getting this method callable? See comment in code block below.
public interface IA{}
public interface IB{
public Integer doWork();
}
public interface IC extends IB{
}
class B implements IB{
Integer doWork(){
return 2;
}
}
class C extends B implements IC{
#Override
Integer doWork(){
return 7;
}
}
//What do I need to do to cast clazz to an object so I can call the derived class' doWork method?
private Integer newClient(Class<T> clazz){
((B) clazz).doWork();
}
Ended up finding a solution:
B.class.cast(clazz);
As for how to ensure you call the derived class' method that overrides the base, that is a native behavior of Java.
Example Program:
public class Foo {
static class A {
int get() { return 0; }
}
static class B extends A {
#Override
int get() { return 1; }
}
public static void main(final String[] args)
{
A a = new A();
B b1 = new B();
A b2 = new B();
printA(a);
printA(b1);
printA(b2);
}
public static <T extends A> void printA(T bObj) {
System.out.println(bObj.get());
}
}
Output:
0
1
1
Note that the output returned from b2::get()::int is the same as b1::get()::int, even though b2 is type A and b1 is type B. This is because even though we only have a reference to the A class in b2, the object implementation is still B.
It seems that you only want to know how to instantiate the Class. Assuming it has a default constructor you can do it this way:
private Integer newClient(Class<B> clazz){
try {
((B) (clazz.getConstructor().newInstance())).doWork();
} catch ...
}
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.
It is possible to narrow return type in overriding methods in Java.
But is it possible to force this narrowing during declaration?
For example, this may be good in the pattern, where inner class is subclassed simultaneously with outer one:
public class Try_ForceNarrow {
public interface IMember {
}
public interface Container<M extends IMember> {
M createMember();
}
public static class A implements Container<A.Member> {
#Override
public Member createMember() {
return new Member();
}
public class Member implements IMember {
}
}
public static class B extends A implements Container<B.Member> {
#Override
public Member createMember() {
return new Member();
}
public class Member extends A.Member {
}
}
public static void main(String[] args) {
}
}
This code fails compile with an error of "The interface Container cannot be implemented more than once with different arguments".
How to avoid this?
Of course, I can just write
public static class B extends A {
#Override
public Member createMember() {
return new Member();
}
public class Member extends A.Member {
}
}
But this way I can forget to override createMember() and violate the pattern.
And what about A<T> implements Container<T> ? You can restrict T further as you wanted...
It appears to me that you want each subclass C to have a createMember factory method that returns a new object that has the same type. The problem is that if C1 provides a factory method that returns C1, and then C2 extends C1, there's no way to force C2 to provide its own method--it could just inherit the one from C1.
I don't think there's a solution that will catch errors at compile time, but maybe you can do something that catches the error at run time:
abstract class MemberBase {
protected abstract MemberBase doCreateMember();
protected abstract Class<?> memberClass();
public MemberBase createMember() {
MemberBase result = doCreateMember();
if (result.getClass() != memberClass()) {
throw new RuntimeException("createMember returned the wrong class");
}
return result;
}
}
public static class A extends MemberBase {
#Override
protected Member doCreateMember() {
return new Member();
}
#Override
protected Class<?> memberClass() {
return Member.class;
}
public class Member implements IMember {
}
}
public static class B extends A {
// If you forget to define this, the doCreateMember in A will be
// inherited, but it will return the wrong class and lead to a
// run-time exception
#Override
protected Member doCreateMember() {
return new Member();
}
#Override
protected Class<?> memberClass() {
return Member.class;
}
public class Member extends A.Member {
}
}
I haven't tested this, and I'm not sure it accomplishes what you are looking for. Plus I may have gotten some syntax wrong. But maybe this, or a modification of it, might be useful.
I believe this fulfills what you are trying to do.
You can create a new abstract class that implements Container which allows you to continually extend or narrow the generics, but you need to declare each class static:
class Try_ForceNarrow {
public interface IMember {
}
public interface Container<M extends IMember> {
M createMember();
}
//The abstract class that lets you continually extend or "narrow"
public static abstract class A<E extends A.Member> implements Container<E> {
public static class Member implements IMember {
}
}
//Here is the first class that extends the Abstract Class A
public static class B extends A<B.Member> { //This class now Implements Container<B.Member>
#Override
public Member createMember() {
return new Member();
}
//Since this is the first extension we will extend A.Member
public static class Member extends A.Member {
}
}
//This is an additional class that extends A but we want to extend the functionality of B.Member also.
public static class C extends A<C.Member> { //This class now Implements Container<C.Member>
#Override
public Member createMember() {
return new Member();
}
//C.Member properly extends B.Member
//if this class is going to be extended this needs to be static
public class Member extends B.Member {
}
}
}
And of course per your requirements if you remove
#Override
public Member createMember() {
return new Member();
}
From the extending sub-classes you get a compile error.
Of course it will fail, your class A implements Container<A.Member>, but your class B extends A but also implements Container<B.Member> which is like class B extends A implements Container<A.Member> Container<B.Memeber> , and in terms of the compiler, there is no difference between Container<A.Member> and Container<B.Member> due to type erasure, after compilation, all generics information will be lost, so there is no <……> after type erasure, only their upper bounds and lower bounds exist, your type parameters are unbounded so they will erase to the same type, after erasure, your class B literally looks like this class B extends A implements Container Container which is fundamentally wrong. You can narrow the return type on overrides which is called covariant return type, this will also create a bridge method in the VM to preserve porlymorphism in Generics.
Say you have a super-class. In that super class you want to pass runtime object of itself (this) as a parameter to an overloaded method. Trick is, this overloaded method is overloaded by sub-class type. When you try to do it, you'll get a
method ... is not applicable(actual argument
... cannot be converted to ... by method invocation
conversion)
Instead you would need to implement the method separately in each subtype (just to get the correct runtime class), which is a lot of duplicate effort when the contents of the method are identical.
e.g:
public class InferTypeTest {
public static void main(String[] args) {
SubClass1 s1 = new SubClass1();
s1.sayHi();
}
public static void sayHi(SubClass1 clz) {
System.out.println("clz 1");
}
private abstract static class SuperClass{
public void sayHi() {
InferTypeTest.sayHi(this);
}
}
private static class SubClass1 extends SuperClass{
}
}
Yes, this is how double dispatch works, you have to override the accept method in each subclass like this:
private static abstract class NodeWithChildren implements DomNode {
/* snip */
public void accept(DomNodeVisitor visitor) {
for (DomNode child : children) {
child.accept(visitor);
}
}
}
private static class BodyNode extends NodeWithChildren {
public void accept(DomNodeVisitor visitor) {
visitor.visit(this);
super.accept(visitor);
visitor.visited(this);
}
}
private static class DivNode extends NodeWithChildren {
public void accept(DomNodeVisitor visitor) {
visitor.visit(this);
super.accept(visitor);
visitor.visited(this);
}
}
}
BodyNode is a NodeWithChildren
DivNode is a NodeWithChidren
NodeWithChildren is a DomNode
DomPrinterVisitor is a DomNodeVisitor
DomNodeVisitor's visitor can visit "BodyNode" or "DivNode, But you are passing "NodeWithChildren" to visit.
Here BodyNode is a NodeWithChildren but NodeWithChildren is not BodyNode/
Theary is if B extends A, you can say B is a A/ not A is a B.
package com.openwaf.test.basic;
public class MethodArgumentTest {
static interface Inf{}
static class One<E extends Inf > implements Inf{
public <T extends One> T get(T k){
return k;
}
}
static class Two<E extends Inf> extends One<E>{ }
public static void test(){
One o=new One<Inf>();
Two t=new Two<One>();
o.<Two>get(t);
}
}
Above code is just for testing purpose. IMHO it should compile without any problem ,but java compiler says
MethodArgumentTest.java:15:
get(com.openwaf.test.basic.MethodArgumentTest.One) in
com.openwaf.test.basic.MethodArgumentTest.One cannot be applied to
(com.openwaf.test.basic.MethodArgumentTest.Two)
o.get(t);
1 error
Can someone please help me out here ?
Ok, as you said this is for testing only I will not ask what this code is good for. The following one compiles but still produces a warning. You were not consequent enough defining the generics:
public class MethodArgumentTest {
static interface Inf {
}
static class One<E extends Inf> implements Inf {
public <T extends One<E>> T get(T k) {
return k;
}
}
static class Two<E extends Inf> extends One<E> {
}
public static void test() {
One<Inf> o = new One<Inf>();
Two<One<Inf>> t = new Two<One<Inf>>();
o.<Two> get(t); /* unchecked warning */
}
}
I think what you need to do is
Two tPrime= o.get(t);