I have an abstract class that is extended by two other classes. In the abstract class I have a method that is implemented differently by those two classes. I want to be able to use that method even if I don't know which concrete class object it is. Is there a way to make the return type the same as what the method was used on not the abstract type?
To make it more concrete, the relevant part of abstract class:
public abstract class State<abstarct> {
public abstract Set<State<?>> findNeighbors();
And for the other two classes:
public class PuzzleState extends State<PuzzleState> {
@Override
public Set<PuzzleState> findNeighbors() {
// making the set
// unrelevant to the question
return set;
}
and
public class QueensState extends State<QueensState> {
@Override
public Set<QueensState> findNeighbors() {
// making the set
// unrelevant to the question
return set;
}
For now it doesn't work of course. I just thought State might be what I was looking for but it isn't. Is there a way to make it work? I want the findNeighbors() to return either Set or Set according to what the method was used on. I want it to be that way because in another part of the program I have a display() method that takes as an input either PuzzleState or QueensState and displays it accordingly.
I don't know if my question was clear enough, but basically I just want to know if there is a way to change the abstract methods return type according to the class.
Why is State
declared as generic ? I dont see any specific value by doing that. If all you want to achieve is to be able to return concrete State
s from subclases , you can as well do this way :
abstract class State {
public abstract Set<? extends State> findNeighbors();
}
and then your sub states as :
class PuzzleState extends State {
@Override
public Set<PuzzleState> findNeighbors() {
Set<PuzzleState> set = new HashSet<PuzzleState>();
return set;
}
}
and
class QueensState extends State {
@Override
public Set<QueensState> findNeighbors() {
Set<QueensState> set = new HashSet<QueensState>();
return set;
}
}
EDIT after OPs first comment :
I have classes for displaying which need to know whether it is a state of Puzzle or the state of Queen
If your processing logic depends upon the knowledge of concrete types and cannot work polymorphically based on the abstract type - then thats most likely a design issue/smell if you want to stick to SOLID design principles.
You can think of refactoring the parts of your code that display the particular state like this :
In each concrete State
( ie PuzzleState
and QueenState
here ) have the logic of returning an interface StateImage
and then in the part of gui where you want to render your states' visual form - use interface ImageRenderer
abstract class State implements StateDisplayable{
public abstract Set<? extends State> findNeighbors();
}
class PuzzleState extends State {
@Override
public Set<PuzzleState> findNeighbors() {
Set<PuzzleState> set = new HashSet<PuzzleState>();
return set;
}
public Image getImage(){
return new PuzzleStateImage(); // implement this for Puzzle !!
}
}
class QueensState extends State {
@Override
public Set<QueensState> findNeighbors() {
Set<QueensState> set = new HashSet<QueensState>();
return set;
}
public Image getImage(){
return new QueenStateImage(); // implement this for QueenState!!
}
}
abstract class Image {
abstract void draw();
}
class PuzzleStateImage extends Image {
void draw() {
// knows what/how to render PuzzleState for a visual component
}
}
class QueenStateImage extends Image {
void draw() {
// knows what/how to render QueenStateImage for a visual component
}
}
interface StateDisplayable {
Image getImage();
}
interface StateRenderer {
void draw(Image state);
}
and then in GUI code
void display(){
StateRenderer renderer = null ; // instantiate
Set<? extends State> states = null; // get your states here ( this is a mix of Puzzle and Queen states - but you dont care about the concrete type )
for(StateDisplayable state : states){
Image stateImage = state.getImage(); // this could be Puzzle or Queen stateImage
renderer.draw(stateImage);
}
}