public class TestClass {
private class Parent {
public Object returnSomething() {
return new Object();
}
}
private class Child extends Parent {
public String returnIt() {
return (String) super.returnSomething();
}
}
private class GrandChild extends Child {
public StringBuilder returnIt() {
return (StringBuilder) super.returnSomething();
}
}
}
My IDE complains about that the method in child clashing with the method in grandchild. The method definitions are different on the return type, so I don't see how they can be clashing.
Can someone help me to understand what Java is not allowing here?
In Java, method overriding is not possible with different return type unless it is covariant return types for overridden methods.
StringBuilder is not a sub class of String so compiler is not allowing to overide the method returnIt()
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
So, if you change return type of returnIt() in Child , it will work,
private class Child extends Parent {
public CharSequence returnIt() {
return (String) super.returnSomething();
}
}
Both methods have same name and input arguments, so the compiler qualifies them as same method, even though their return type differs.
Overriding method and changing only the return type is not acceptable by the compiler.
In order to return different type, you can :
use new method name, like (returnItAsStringBuilder)
add additional argument to your method
use generic types:
Example:
private class Child<T> extends Parent {
public T returnIt() {
return (T) super.returnSomething();
}
}
private class GrandChild extends Child<StringBuilder> {
}
But looking at this code, it will always fail. You will get ClassCastExcception, because in Parent class you are creating plain Object, which cannot be casted to anything else.
You should consider abstract parent class;
private abstract class Parent <T>{
public abstract T returnSomething();
}
private class GrandChild extends Parent<StringBuilder> {
#Override
public StringBuilder returnSomething() {
return new StringBuilder();
}
}
Related
I'm pretty new to java and not particularly sure how to initialize a generic type / child class from the 'base' class.
Essentially I have a bunch of classes that extend the abstract class BaseClass that need to be initialized and added to the instance Map if a key is not present.
The child class is re-used multiple times but is dynamically created based on the key parameter.
I would like to avoid reflection and don't mind changing the template if it's not 'the Java way'.
What I currently have:
public abstract class BaseClass<T> {
protected Map<String, T> instance = new HashMap<String, T>();
public T Get(String key) {
if (this.instance.containsKey(key)) {
return this.instance.get(key);
}
T item = new T(key); // Obviously this line errors but you get the idea
instance.put(key, item);
return item;
}
}
// Example top class which extends my base class
public class TopClass extends BaseClass<TopClass> {
public TopClass(String key) {
// Do unique initialization stuff
}
}
Since generic types are erased at runtime, you cannot do this. You can instead use a Class variable as follows:
public T Get(Class<T> clazz, String key) throws Exception {
if (this.instance.containsKey(key)) {
return this.instance.get(key);
}
T item = clazz.getDeclaredConstructor(String.class).newInstance(key);
instance.put(key, item);
return item;
}
I have another approach to this.
Have an interface MyInterface.
public interface MyIinterface{
public void doSomething();
}
Create an many implementations of this interface.
#Component
public class MyImplementation1 implements MyInterface{
#Override
public void doSomething(){
}
}
Use spring core jars in the dependency.
Annotate all the implementations with #Component.
#Component
public class MyImplementation1 implements MyInterface{
.
.
Have a method in some Util class that will get you the implementation based on a string key.
public static MyInterface getImplementation(String name){
ApplicationContext context;
return context.getBeanByName(name);
}
I can declare an InterfaceA, which can contain any methods. But important to say, that it must contain methods (they are required by framework), that return Return<T extends Data>. Each T is present only once and there no method that return Data itself:
class InterfaceA {
Return<DataA> returnData...(...);
Return<DataB> returnData...(...);
...
}
And I have ClassB:
class ClassB<T extends Data> {
InterfaceA a;
void process() {
Return<T> data = a.returnData(...);
...
}
}
I need some way to pick proper method from InterfaceA dependently on type T of ClassB. How can I do that?
I would do it by having a method in the interface that will return the type of the Object that extends data and then use some if statements or a switch-case to then run a method depending on what this method returns.
Eg.
public class Test extends Data{
private final String identifier;
public Test(String identifier){
this.identifier = identifier;
}
public String getIdentifier(){
return identifier;
}
}
Not sure if that's any help bro as others said it's not the best design pattern.
In this case my IDE shows compilation error in return statement.
public class Base<T extends Base>{
public T get(){
return this;
}
}
When I add a typecast as in code bellow everything works fine, however I don't get why typecast is needed.
public class Base<T extends Base>{
public T get(){
return (T) this;
}
}
Doesn't Java replace all bounded generic occurrences with bounded type? Can someone explain what is going on under the hood and why typecast is needed?
Edit 1.
Thanks to Lothars and algrid answers it is now clear that this standalone case can cause ClassCastException. This is not safe so Base should probably be abstract.
The intent of this is to create a base class for Builder classes so that extended methods would return the type of the extending class. This is needed for method chaining. In the example bellow the return type of child.setParamOne(1) will be Child despite the fact that it is defined above in the inheritance hierarchy.
Is this code safe? Do you have any suggestions or alternatives for approaching this problem?
public abstract class Base<T extends Base>{
int paramOne;
public T setParamOne(int param){
this.paramOne = param;
return (T) this;
}
}
public final class Child extends Base<Child> {
int paramTwo;
public Child setParamTwo(int param){
this.paramTwo = param;
return this;
}
}
public static void main(String[] args) {
Child c = new Child()
.setParamOne(1)
.setParamTwo(1);
}
Why do you think that your this is of the type T? It's of the type Base<T>.
Try to run the following code and you'll get ClassCastException:
public class Main {
public static void main(String[] args) {
Base<Child> b = new Base<>();
// b.get() returns an instance of Base, not Child (however it's mistakenly cast to Child)
Child1 c = b.get();
}
public static class Base<T extends Base>{
public T get(){
return (T) this;
}
}
public static class Child extends Base {
}
}
The reason for this error is the same as the error being created for code like this:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = is;
}
Just with generics. To get rid of the complier error you can do the cast as you did in your code:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = (ByteArrayInputStream) is;
}
But this will fail during runtime if the passed inputstream is not a ByteArrayInputStream or a class derived from it. The same will happen with your code. Unless you only create instances of Base<Base> the cast will lead to an error when calling get.
In your example:
public class Base<T extends Base>{
public T get(){
return this;
}
}
the return statement is incorrect, because this is an instance of Base<T> and not T.
If your aim is to return the instance itself (by the way, I'm not sure why you would be doing this), the code should look like this:
public class Base<T extends Base>{
public Base<T> get(){
return this;
}
}
If your aim is to return the parameterized type, then you will probably not be able to do that. The parameterized type itself is not an instance within the Base class, but, again, just the parameterized type. If that is what you need, you can get the parameterized type class using reflection.
The conversion is unsafe because this (which has type Base<T>) may not be a T. We only know that T is a Base, but not the other way around.
There is no way to represent a "self type" in Java. So what you want to do is impossible. Instead, you can make an abstract method that forces implementing subclasses to provide a way to return a T:
public class Base<T> {
public abstract T get();
}
public final class Child extends Base<Child> {
public Child get() {
return this;
}
}
I wan't to make a method declaration in a superclass called 'dataItem' so that all subclasses that implement that method must have a return type that is of that implementing class. Is that possible?
For example if I have class 'Experiment' which implements 'dataItem' and I have method newItem() . Which for 'Experiment' should only be able to return 'Experiment' datatype and not any other implementation of 'dataItem'.
You can't force a class method to return the type it is a member of. You have to actually specify it.
public class DataItem {
public DataItem getItem() {return null;}
}
public class Experiment extends DataItem {
#Override
public Experiment getItem() {return null;}
}
This works because Experiment is a sub class of DataItem and can therefore be used anywhere a DataItem could be used.
I suppose you're looking for this:
public interface dataitem<T>
{
public T newItem();
};
public class Element implements dataitem<Element>
{
#Override
public Element newItem()
{
return new Element();
}
}
I tried to create a class that extended File class (java.io.File) and implement TreeNode interface like below:
public class mTreeNode extends File implements TreeNode{}
and tried to implement TreeNode methods but a conflict occurred.
public String getParent(){} on File class has conflict with public TreeNode getParent() on TreeNode interface on return type.
How can we solve it ? (for example why can't use Object class for return type !)
Finally I decided use a file object on my class.
Since TreeNode is an interface, you are required to implement that method with its exact signature. It's a contract between the class that implements it and the outside world that is enforced by the compiler.
You can read more about it here.
Classes that implement interfaces may declare covariant return types. So the return types in classes that implement interfaces must either match the interface or be a sub-class of the interface.
e.g.
class Foo {
public String toString() {
return "foo";
}
}
interface Interface {
Foo getFoo();
}
class Bar extends Foo {
public String toString() {
return "bar";
}
}
class Concrete implements Interface {
public Bar getFoo() { // <============= Covariant return type
return new Bar();
}
}
public class Example {
public static void main(String[] args) {
System.out.println(new Concrete().getFoo());
}
}
The class will print bar.