I am trying to learn Subtyping in Java and I am not an better person in generics so I am getting this issue or doubt-
import java.util.ArrayList;
import java.util.Collection;
interface Animal<T>{}
class Lion implements Animal<Lion>{}
class Butterfly implements Animal<Butterfly>{}
class Cage<T>{
public <T> void addAnimal(T t){
}
}
interface CageAnimal<E> extends Collection<E>{}
public class SubType<T> {
public <T> SubType() {
Lion lion = new Lion();
Butterfly butterfly = new Butterfly();
/**
* **Here inside Lion cage, we added Butterfly : WRONG**
*/
Cage<Lion> cageLion = new Cage<Lion>();
cageLion.addAnimal(lion);
cageLion.addAnimal(butterfly);
CageAnimal<Lion> cageAnimalLion = (CageAnimal<Lion>) new ArrayList<Lion>();
cageAnimalLion.add(lion);
//cageAnimalLion.add(butterfly);//Butterfly is Not Supposed to add here as it is the cage of Lion
}
}
In the above example when I declare Cage , why I am able to add Butterfly and in the Same case when I made CageAnimal type, I am not able to add any Buttefly
Cage<Lion> cageLion = new Cage<Lion>();
cageLion.addAnimal(lion);
cageLion.addAnimal(butterfly);
and in case of Cage
Cage<Animal> cageAnimalLion = new Cage<Lion>();
cageAnimalLion.addAnimal(lion);
cageAnimalLion.addAnimal(butterfly); //Throwing Compile Error
This line
public <T> void addAnimal(T t){
should probably be
public void addAnimal(T t){
Declare Cage class like this:
class Cage<T extends Animal> {
public void addAnimal(T t) { ... }
}
If you declare the addAnimal method in the following way...
public void <T> addAnimal(T t)
... you are "hiding" the T type parameter with a different type parameter with the same name. It is the same as if you declared the method like this:
class Cage<T extends Animal> {
public void <X> addAnimal(X t) { ... }
}
...which is obviously not doing its job. On the other hand, in the first version I wrote, both the T in declaration of the class and the method are the same.
Moreover declaring <T extends Animal> bound ensures that the cage can only be of type that extends an Animal, i.e. Cage<Lion>, Cage<Butterfly>, but Cage<String> is illegal.
And of course, you cannot cast an ArrayList to CageAnimal, that will fail at runtime with a ClassCastException, because ArrayList in not a subtype of CageAnimal.
Because CageAnimal and Cage are very different things. Looks how you've defined generic parameter for Cage:
public <T> void addAnimal(T t){
}
This <T> you put on the method, means that method has its own generic parameter, different from the one you've defined in class. If you remove it from method signature it will use generic parameter of the class.
E.g.
public void addAnimal(T t)
Your problem is that fundamentally your Cage will accept any T, and therefore any Animal. The various T's don't all refer to the same value of T, they're variables local to the class or method.
What you could write is something like this:
public class Cage<T> {
public void addAnimal(Animal<T> caged) {
}
}
Now you will at least get compiler errors in the common case of:
Cage<Lion> c=new Cage<Lion>();
c.add(new Butterfly()); // should error AFAIK
However it will be reduced to a warning in case of:
Animal butterfly=new Butterfly();
Cage<Lion> c=new Cage<Lion>();
c.add(butterfly); // warning about raw types... IIRC
Because, fundamentally Cage will still accept any Animal.
EDIT: Note that the earlier mentioned answer of removing the <T> local to the addAnimal method will work better for this purpose.
By declaring public <T> void addAnimal(T t) you're parameterising the method as well as the class Cage. This T has no relation to the T in Cage<T>.
You can either have:
class Cage<T extends Animal<T>> {
public void addAnimal(T animal) {
}
}
or, if you want the Animal returned then have:
class Cage<T extends Animal<T>> {
public T addAnimal(T animal) {
}
}
class Cage<T>{
public <T> void addAnimal(T t){
}
}
The Cage class has a generic method addAnimal. The generic type associated with the method causes the generic type associated with the class to be ignored and the type of the parameter to be used as the generic type for the method.
Try executing the following example to see what is happening:
public class TestCage {
/**
* #param args
*/
public static void main(String[] args) {
Cage<String> cage1 = new Cage<String>();
cage1.addAnimal(new String("test1"));
cage1.addAnimal(new Integer(1));
cage1.addAnimal2(new String("test2"));
//cage1.addAnimal2(new Integer(1)); //Uncomment to throw error
}
}
class Cage<T>{
public <T> void addAnimal(T t){
System.out.println("T: " + t.getClass().getName());
}
public void addAnimal2(T t){
System.out.println("T: " + t.getClass().getName());
}
}
In summary, by adding a generic method to the class, the generic type parameter of the class is ignored and the type of the parameter passed into the method is used as the generic type parameter of the method.
Try to take the <T> out of public <T> void addAnimal(T t).
Related
I am trying to extend a Generic class with bounded typed parameter with another generic class with typed parameter which follows the super generic's typed parameter.
Super Generic with upper bounded type parameter
public abstract class Cage<T extends Animal> {
protected Set<T> cage = new HashSet<T>();
public abstract void add(T animal);
public void showAnimals() {
System.out.println(cage);
}
}
Generic class I want to create with a specific bounded type e.g. Lion
I tried the below code but I am getting an error The type parameter Lion is hiding the type Lion and Syntax error on token "extends", , expected
For the add() method in LionCage class I am getting the error The method add(Lion) of type LionCage must override or implement a supertype method
LionCage class meant for Cage<Lion extends Animal>
public class LionCage<Lion extends Animal> extends Cage<T extends Animal> {
#Override
public void add(Lion l) {
cage.add(l);
}
}
My Animal class with its subclasses Lion, Rat, etc are defined in Animal.java
public class Animal {
public String toString() {
return getClass().getSimpleName();
}
}
class Rat extends Animal {}
class Lion extends Animal {}
Since I am getting the errors, I am guessing that the approach I am following may not be correct. In that case, is there any standard way of implementing this scenario in Java generics?
Your LionCage class doesn't have to be generic, but it has to pass a concrete type paramter (Lion) in the extends clause:
public class LionCage extends Cage<Lion> {
#Override
public void add(Lion l) {
cage.add(l);
}
}
This seems to be a compiler issue, or maybe this is there by design.
ClassA is a class with two generics. ClassB will extend ClassA by providing one solid generic type, but still expose another one.
In the following example, E will be passed in type that will extend ClassA, so when any method is called, then returned type will still be the subtype which enables to call the subtype method if needed. The motivation behinds this is to do a builder pattern, e.g.
ClassB b = new ClassB<String>().m1().m2().m3().m4()......
public class ClassA<E, T> {
public final E e;
public final T t;
public ClassA(T t) {
this.e = (E)this;
this.t = t;
}
public E printA() {
System.out.println("AAAAAA");
return e;
}
}
public class ClassB<T> extends ClassA<ClassB, T> {
public ClassB(T t) {
super(t);
}
public ClassB printB() {
System.out.println("BBBBBB");
return this;
}
public static void main(String[] args) {
ClassB<String> classB = new ClassB<>("Hello World");
// classB.printA().printA().printA(); // This will fail, after the second printA() return Object type instance instead of ClassB.
System.out.println(classB.printA().printA().getClass()); // This will print "class ClassB", so the class information it still there.
((ClassB)classB.printA().printA()).printA(); // Casting the instance to ClassB again will make it work again.
}
}
The problem is that when you call the method two times, the return instance type will be lost, so it will be an Object type, and cannot call any ClassA/B method without casting it. This is super weird.
Any idea?
Your class ClassB is a generic one, but you are opting out of generics when not providing a type parameter.
And you are doing exactly that here
public class ClassB<T> extends ClassA<ClassB, T>
^^^^^^
and here
public ClassB printB()
^^^^^^
So simply change these lines to
public class ClassB<T> extends ClassA<ClassB<T>, T>
^^^
and
public ClassB<T> printB()
^^^
Then it will work.
I'm primarily a Java programmer, so this would be one of those "what is this thing from Java equivalent to in C#" questions. So, in Java, you can restrain a Class type argument at compile time to extend a certain super-class, like so:
public <T extends BaseClass> void foo(Class<T> type) {
...
}
and even
public <T extends BaseClass> T foo(Class<T> type) {
...
}
You can even chain multiple interfaces:
public <T extends BaseClass & BaseInterface1 & BaseInterface2> void foo(Class<T> type) {
...
}
How is this done in C#? I know you can use "where T : BaseClass", but this is only applicable when you have an instance T. What about when you only have a Type instance?
EDIT:
For explanation, here is what I would like to do:
ASSEMBLY #1 (base.dll):
abstract class BaseClass {
abstract void Foo();
}
ASSEMBLY #2 (sub1.dll, references base.dll):
class SubClass1 : BaseClass {
void Foo() {
// some code
}
}
ASSEMBLY #3 (sub2.dll, references base.dll):
class SubClass2 : BaseClass {
void Foo() {
// some other code
}
}
ASSEMBLY #4 (main.dll, references base.dll):
class BaseClassUtil {
static void CallFoo(Type<T> type) where T : BaseClass {
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public static void Main(String[] args) {
// Here I use 'args' to get a class type,
// possibly loading it dynamically from a DLL
Type<? : BaseClass> type = LoadFromDll(args); // Loaded from DLL
BaseClassUtil.CallFoo(type);
}
So, in this example, I don't care what class the 'type' variable represents, as long as it is derived from BaseClass, so once I create an instance, can call Foo().
The parts that are not vaild C# code (but rather some Java mockup) are the "generic" Type classes: Type<T> and Type<? : BaseClass>.
No, there is no way to enforce at compile time that a Type be assignable to a generic type. If I understand correctly, what you want is:
void Foo<T>(Type type) { ... } //compile time error if an instace typed `type` is not assignable to `T`.
Which means:
void Foo<IFormattable>(typeof(string)); //ok
void Foo<IDisposable>(typeof(string)); //compile time error
Evidently at runtime it is trival, but the language has no support for this at compile time.
From what I understood you are talking about generic type constraint
public void Foo<T>(Type type) where T:BaseClass, BaseInterface1, BaseInterface2
{
//your code
}
Here another article:Constraints on Type Parameters (C# Programming Guide)
When you define a generic class, you can apply restrictions to the
kinds of types that client code can use for type arguments when it
instantiates your class. If client code tries to instantiate your
class by using a type that is not allowed by a constraint, the result
is a compile-time error.
EDIT:
Here your example. Now if you try to call BaseClassUtil.CallFoo<T> with something different from BaseClass and his derived classes you will receive an compile error. Here full example in dotNetFiddle. So the tricky part is the restriction of your class should happen in the Util class
public static void Main(string[] args)
{
//so your LoadFromDll method should return Type. Type doesn't have generic implementation !
Type type = typeof(SubClass1);
BaseClassUtil.CallFoo<BaseClass>(type);
Type type2 = typeof(SubClass2);
//you can write BaseClassUtil.CallFoo<SubClass2>(type2); if you want
BaseClassUtil.CallFoo<BaseClass>(type2);
}
public class BaseClassUtil
{
public static void CallFoo<T>(Type type) where T : BaseClass
{
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public class TestClass
{
public int ID { get; set; }
}
public abstract class BaseClass
{
public abstract void Foo();
}
public class SubClass1 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 1");
}
}
public class SubClass2 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 2");
}
}
I have some troubles with a method having a typed List parameter, inherited from another (typed) class.
Let's keep it simple :
public class B<T> {
public void test(List<Integer> i) {
}
}
The B class has a useless generic T, and test() want an Integer List.
Now if I do :
public class A extends B {
// don't compile
#Override
public void test(List<Integer> i) {
}
}
I get a "The method test(List) of type A must override or implement a supertype method" error, that should not happen.
But removing the type of the list works... although it doesn't depend on the class generic.
public class A extends B {
// compile
#Override
public void test(List i) {
And also defining the useless generic below to use the typed list
public class A extends B<String> {
// compile
#Override
public void test(List<Integer> i) {
So I'm clueless, the generic of B should have no influence on the type of the test() list. Does anyone have an idea of what's happening?
Thanks
You're extending the raw type of B, not the generic one. The raw one effectively does not have a test(List<Integer> i) method, but a test(List) method.
If you switch to raw types, all generics are replaced by raws, regardless of whether their type was filled in or not.
To do it properly, do
public class A<T> extends B<T>
This will use the generic type B<T>, which includes the method you want to override.
When you remove use a class without generics (and use it raw), all generics from class methods are forgotten.
Due this reason when you inform the generic type on the second case you get it working.
This:
class T<G> {
public void test(G g);
}
in this case:
class A extends T {
}
will look like this:
class T {
public void test(Object g);
}
This was a java puzzle presented on Google IO 2011 you can see video here
The code below is a simplified version of the pattern my project is using. The standard pattern we use is to have a Writer for each object type. For the subtypes of one abstract type (in this example Animal), I'd like an enum to serve as a lookup for the correct writer.
abstract class Writer<T> {
abstract void write(T value);
}
abstract class Animal {
abstract AnimalType getType();
}
class Cat extends Animal {
AnimalType getType() { return AnimalType.CAT; }
}
class CatWriter extends Writer<Cat> {
void write(Cat value) { }
}
// The AnimalType stores a reference to the correct writer for the Animal subclass
enum AnimalType {
CAT(new CatWriter());
Writer<? extends Animal> writer;
Writer writerThatWorksWithWarning;
Writer<Animal> writerThatWorksButCantBeAssigned;
AnimalType(Writer<? extends Animal> writer) {
this.writerThatWorksWithWarning = writer;
this.writer = writer;
// ERROR: Incompatible Types
this.writerThatWorksButCantBeAssigned = writer;
}
}
Sample use case:
class Test {
public static void main(String... args) {
Animal value = new Cat();
// ERROR: write (capture<? extends Animal) in Writer cannot be applied to (Animal)
value.getType().writer.write(value);
// WARNING: Unchecked call
value.getType().writerThatWorksWithWarning.write(value);
// This line works fine here - but can't be assigned above
value.getType().writerThatWorksButCantBeAssigned.write(value);
}
}
I think that my problem is similar to the problem in this question: Java Generics with wildcard, however I can't tell how to solve it.
I've put the inline errors and warnings I get in the comments.
Any ideas?
I think the issue here is that you can't represent a type hierarchy with an enum, so there's no way to tell the type system that for enum { CAT, DOG; } the CAT should type to CAT extends Animal and the DOG types to DOG extends Animal. So But since you have a class hierarchy already, why not use that? i.e. something like :
public interface Writer<T> {
public void write(T t);
}
public abstract class Animal<T extends Animal<T>> {
public abstract Writer<T> getWriter()...
}
public class Cat extends Animal<Cat> {
#Override
public Writer<Cat> getWriter()...
}
It seems to me that what you're really using the enum for is something more like a hashmap of <Class, Writer<Class>>, sort of a built in singleton. You can do this, but only by hiding the types.
I would have animals unaware of writers. they are animals after all.
You can have a Map<Class,Writer>, and for each entry in it, you maintain that the key Class<X> and value Writer<X> are about the same type X. We can't express that relation in types, so casts must be done at some places. If looking up fails for a type (say Cat), try looking up again with its super types (Animal)
A type safe public API can be designed like
static public <T> void registerWriter(Class<T> type, Writer<T> writer)
static public <T> Writer<? super T> getWriter(Class<T> type)
Suppose we don't have a Writer directly mapped to Cat, but we do have a Writer<Animal> for Animal, then that writer will be returned for Cat.class. That is ok, because that writer does accept all animals.
This convenient method can be provided:
static public static void write(Object obj)
from the type of the object, a suitable writer can be found, and the writer will accept the object.
Try this instead,
enum AnimalType {
CAT(new CatWriter());
private Writer<? extends Animal> writer;
AnimalType(Writer<? extends Animal> writer) {
this.writer = writer;
}
public Writer<Animal> getWriter() {
return (Writer<Animal>)writer;
}
}
Moreover, I am not sure what are you up to. But I believe that Visitor pattern will come handy in this case.
Problem with the above solution, the code below will break the thing.
Animal cat = new Cat();
Animal dog = new Dog();
cat.getType().getWriter().write(cat);
// java.lang.ClassCastException in the write() method's argument
cat.getType().getWriter().write(dog);