Superclass static method returning 2 different subclasses - java

This is bad programming practice but I've been asked to do it as part of a larger assignment.
I am creating a superclass and then 2 subclasses. There is a static method in the superclass that should return either of the 2 subclasses depending on the result. How would I go about writing this method?
For example I need to do something like this
public abstract class Superclass{
public static subclass? createFromFilename(String fileName){
if(1==1)
return subclass1;
else
return subclass2;
}
}
Is this even possible?

I am not sure if this is what you are looking for, but if you want to return the class type, you can do that by writing the subclass name and appending a .class to get the class type. A proper return type would be the type Class with a generic limiting the result to Superclass and its subclasses.
public static Class<? extends Superclass> createFromFileName(String fileName) {
if (fileName.equals("A")) {
return SubclassA.class;
} else {
return SubclassB.class;
}
}
If, however, you want to return an object of the respective class, you can do that by simply return a new instance and set the return type to Superclass:
public static Superclass createFromFileName(String fileName) {
if (fileName.equals("A")) {
new SubclassA();
} else {
new SubclassB();
}
}

You can do it like this
public abstract superClass
{
public static superClass getBaseClass(...)
{
if(...)
{
return new baseClass1();//baseClass1 should derive from superClass
}
else
{
return new baseClass2();//baseClass2 should derive from superClass
}
}
}
You can now do this
superClass sc=superClass.getBaseClass(..);
if(sc instanceof baseClass1)
{
baseClass1 bc1=(baseClass1)sc;
//work with bc1...
}
if(sc instanceof baseClass2)
{
baseClass2 bc2=(baseClass2)sc;
//work with bc2...
}

You are trying to determine the return type as a result of the execution of the method. This is not possible to do. So you have to bear having the common superclass return type. BTW if you know at compile time the proper return type, why dont you change (having 2 methods?) the method to reflect that?

Related

How to call a getter from a subclass which hasn't an abstract declaration in the superclass

I have a superclass that I instantiate as ArrayList. A subclass of the superclass is saved in the ArrayList. Now I want to get the information out of the ArrayList, but the getter for the attribute is only available in the subclass. I can get the information (which are saved in the ArrayList too) from other classes that have an abstract declaration in the abstract superclass. However, I don’t like to implement “dummy” methods in all classes which extend the superclass.
Code truncated:
public class Question{
QuestionPool() {
questionPool = new ArrayList<Question>();
ClozeQuestions q15 = new ClozeQuestions("Erster Text", "Zweiter Text");
questionPool.add(q15);
}
public int startQuiz{
System.out.printf("Q: %s", questionPool.get(i).?????
}
}
public abstract class Question {
String question;
public String getQuestion() {
return question;
}
}
public class ClozeQuestions extends Question {
ClozeQuestions(String questionPart1, String questionPart2){
this.questionPart1 = questionPart1;
this.questionPart2 = questionPart2;
}
public String getQuestionPart1() {
return questionPart1;
}
public String getQuestionPart2() {
return questionPart2;
}
}
To circumvent the problem I implemented a "helper method" in the Question class:
ClozeQuestions question2ClozeQuestion = new ClozeQuestions();
return question2ClozeQuestion.getQuestionPart1();
}
Which I call from the QuestionPool class:
System.out.printf("Q: %s", questionPool.get(i).getQuestionPart1());
Since the objects in the list are stored as the type of the superclass, you will have to cast it to the subclass before you can access the subclass methods. This is what type casting looks like:
(Subclass)objectOfSuperclassType
And to be safe, you should type check before casting which can be done with 'instanceof':
if (objectOfSuperclassType instanceof Subclass) {
Subclass objectOfSubclassType = (Subclass)objectOfSuperClass
}
And finally, an example of how to implement this when dealing with a list:
List<Superclass> myList = new ArrayList<>();
myList.add(new Subclass());
if (myList.get(0) instanceof Subclass) {
System.out.println( ((Subclass) myList.get(0)).getSomeString() );
}
Although this may answer your question, the code you provided does not require the List to be initialized with the superclass at all. If you were to have two different subclasses and stored both of them in the same list, only then would it make sense to do this.

Why cast is needed when returning this from self-bound generic in Java

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;
}
}

Declare a method to return this (java)

It is easy to define a method returning the same value as the argument:
class A {
public static void main(String[]args) {
Derived sth = new Derived();
String x = sth.foo("hello");
System.out.println(x);
Derived resultTypeIsKnown = sth.foo(sth); // <==== !!!
System.out.println(""+resultTypeIsKnown);
}
}
class Base {
<T>T foo(T t)
{
return t;
}
}
class Derived extends Base {
}
You see that the compiler knows that although foo() is declared in the Base class, the compiler knows that sth.foo(sth) returns an instance of the Derived class:
Derived derivedRatherThanBase = sth.foo(sth);
How do I declare that the return value is of the same class as the object whose method is called? (In particular, for a method that always returns this?)
Something like:
class Base {
<?ThisClass?> bar() {
return this;
}
}
I can write
<T>T getThis(T t)
{
return (T)this;
}
but x.getThis(x) uses an extra argument and produces a warning.
UPDATE OMG, what they are doing in the "possible duplicate"... But I already have the base class, it's a descendant of Collection. In other words, it is required that class Base extends Collection and knows its class.
And the real life code where I want to know THISCLASS already is very complex.
UPDATE2 As to the XY problem, what I want is:
class MyVerySpecialCollectionBase extends ... {
...
THISCLASS addAll(Collection items) { ... }
}
class MyExtendedVerySpecialCollection extends MyVerySpecialCollectionBase {
...
}
// use:
MyExtendedVerySpecialCollection x =
new MyExtendedVerySpecialCollection(aLotOfArgs)
.addAll(list1)
.addAll(list2);
The proposed solution sounds too complex and even less acceptable than either (1) redefining addAll() in each derived class or (2) making x.addAll(list1).addAll(list2); a separate statement.

Overriding a method with different return types in java?

I have read a book and it says I can override a method if it has the same signature. according to the book the signature of a method is Method_Name + Parameters passed.
as per the book, i can override a method which has different return types. Is it actually possible to override a method with different return type in Java? because i have done a some search on the net i found people saying that to override a method the return type should be same as well.
according to the book it also says the java will throw a compile error when we try to overload a method with same method name and parameters but different return types since the signature means only the method name and parameters. If this is true, we should be able to override a method with different return type.
Please help me to understand this. Thanks in advance.
You can return a different type, as long as it's compatible with the return type of the overridden method. Compatible means: it's a subclass, sub-interface, or implementation of the class or interface returned by the overridden method.
And that's logical. If a method returns an Animal, and your derived class returns a Cow, you're not breaking the contract of the superclass method, since a Cow is an Animal. If the derived class returns a Banana, that isn't correct anymore, since a Banana is not an Animal.
Your parent class has made a promise to the outside world. For example, the method:
public Price calculatePrice(Items[] items).
It tells the world to expect a Price.
If you enhance that functionality in your subclass, you still have to keep your parent classes' original promises for it.
You can add overloaded ways of calculating:
public Price calculatePrice(Items[] items, Integer minimumCharge).
You can even improve your parent's promises by using a MORE specific return type:
public AccuratePrice calculatePrice(Items[] items, Integer minimumCharge).
But you must return at least the type that your parent promised.
The same goes for Exceptions in the method declaration too.
Yes, it is possible since Java 5, it is called covariant return type. The return type should be a subcass of super class method return type (primitive types are not allowed). Example
class X implements Cloneable {
#Override
protected X clone() {
try {
return (X) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error(e); // can never happen
}
}
}
Here is an example:
class Base {
public Number test() {
return 0;
}
}
class A extends Base {
public Long test() {
return 1L;
}
}
Your overriden method can have same type or the sub-type of the
original return type which is called as covariant return.
If you change the return type of the overriden method to something else which is not a sub-type of the original type, then you'd get a compile time error.
Yes we can override different return types but they should be subclass.
public class Shape {
public Shape area(Integer i) {
System.out.println("Sape Area");
System.out.println("Integer");
return null;
}
}
package com.oops;
public class Circle extends Shape {
public Circle area(Integer i) {
System.out.println("Circle Area");
System.out.println("int");
return null;
}
}
// Covariant Overriding
public class Parent {
public Parent(){}
String parentName;
public Parent(String parentName){
this.parentName=parentName;
System.out.println(this.parentName);
}
public Parent show(){
return new Parent("Parent");
}
}
public class Child extends Parent{
public Child(){}
String name;
public Child(String name){
this.name=name;
System.out.println(this.name);
}
public Child show(){
return new Child("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent parent=new Child();
parent.show();
Parent parent1=new Parent();
parent1.show();
}
}

Java generics (template) specialization possible (overriding template types with specific types)

I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.
In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.
Merely providing a type-specific overload of a method produces a "method is ambiguous" error:
e.g.:
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Is the only option to subclass the generic class with a specific type (see StringBar in the following example?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.
I'm guessing I could check the instanceof in the Bar.get() method, e.g.
T get(T t) {
if (t instanceof String) return "";
if (t instanceof Integer) return 0;
else return null;
}
However I've been taught to avoid instanceof and use polymorphism when possible.
All things considered, the concensus appears to be that the StringBar method mentioned in the question is the only way to go.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".
BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the class of the object to be processed:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.
The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.
See an explanation by Jon Skeet's:
What is the concept of erasure of generics in java?
Java Generics - Types erasures - when and what happens?
You can subclass Bar<T> and create StringBar (note I removed the static keyword) and override get() method.
public class BarString extends Bar<String> {
#Override
public String get(String x) {
return "";
}
}
Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.
Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
I don't see why you need to specialize for a String here.

Categories