I have a class for a tile.
public abstract class Tile {
public Position pos;
public char chr;
}
And many different classes that extend tile. Each subclass has its own identifier char.
Now I have a function that receives a char and a position and needs to create and return a new instance of a tile subclass fitting the char in given position.
Is there an elegant OOP way to do this other than run through all of the options?
So for me use of an identifier like this is a bit of a design flaw.
You could add something like the following to the abstract class:
public static Tile factory(char identifier, Position p) {
Tile value = null;
switch (identifier) {
case `x`: {
value = new XTile(p);
break;
}
default {
// throw an exception or provide a default
}
}
return value
}
Effective Java (third edition) Item 21 "Prefer Class hierarchies to tagged classes" might provide an alternative to your design.
Related
Assume I want to define types that are similar in structure, but differ in a parameter that could be an integer or could be something else.
Is it possible in Java to define a family of classes parameterized by an integer or even an arbitrary object?
Consider the following pseudocode (which does not compile):
/**
* String of a certain length n and a method to reduce to length n-1
*/
public class StringN<int n> {
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN<n-1> reduce() {
return new StringN<n-1>(s.substring(0, s.length() - 1));
}
#Override
public String toString() {
return str;
}
}
Other even more natural examples that come to my mind are tensor-products in math, so where to put the parameter 'n', if one wants to define e.g. the space R^n as a Java class or in functional programming the 'arity' of a Function<>-space. So how to define a family of classes with different arity, parameterized by n?
If this is not possible in Java, does this concept exist in other more functional languages and what is the proper name for it? (like maybe 'parameterized class'?)
Edit: as a reaction to comments, the last part was just to know the general name of such a concept, not to make a detour to other languages.
Alas, Java requires type parameters to be types (actually, it even requires them to be reference types), and since all integers are of the same type, you not get the compiler to distinguish generics depending on the value of an integer.
The usual workaround is to declare a separate type for each possible (or needed) value. To share structure, you can use an abstract base class. And if the base class needs any concrete types, the subclasses can pass them as type parameters:
abstract class StringN<S extends StringN<S,P>, P extends StringN<P,?>>
implements Comparable<S> {
final String value;
protected StringN(String value, int n) {
if (value.length() != n) {
throw new IllegalArgumentException(value);
}
this.value = value;
}
#Override
public int compareTo(S o) {
return value.compareTo(o.value);
}
abstract P newP(String value);
public P removeLast() {
return newP(value.substring(0, value.length() - 1));
}
}
class String0 extends StringN<String0, String0> {
protected String0(String value) {
super(value, 0);
}
#Override
String0 newP(String value) {
throw new UnsupportedOperationException();
}
}
class String1 extends StringN<String1, String0> {
protected String1(String value) {
super(value, 1);
}
#Override
String0 newP(String value) {
return new String0(value);
}
}
class String2 extends StringN<String2, String1> {
protected String2(String value) {
super(value, 2);
}
#Override
String1 newP(String value) {
return new String1(value);
}
}
public class Test {
public static void main(String[] args) {
String2 s2 = new String2("hi");
String1 s1 = s2.removeLast();
s1.compareTo(s2); // compilation error: The method compareTo(String1) is not applicable for the arguments (String2)
}
}
As you can see, as long as the set of values is finite and known up front, you can even teach the compiler to count :-)
However, it gets rather unwieldy and hard to understand, which is why such workarounds are rarely used.
Yours is an interesting question, but I think you went too far in assuming that the solution to your need is necessarily a parametrized class.
Parametrized classes are composition of data types, not values.
Since you do not require the compile to enforce any additional static type checkings on your code, I think a programmatic solution would be enough:
First step: Move your pseudo-parameter "int n" to a final variable:
public class StringN {
private final int n;
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN reduce() {
return new StringN(s.substring(0, s.length() - 1));
}
#Override
public String toString() {
return str;
}
}
Of course, this do not compile yet. You must initialize the n variable on every constructor (declarations and callings).
If you feel uncomfortable with the fact of exposing the parameter n as part of the public constructors calling, that can be solved restricting the constructors to package access, and bringing the construction responsibility to a new Factory class, which must be the only public way to create StringN objects.
public StringNFactory
{
private final int n;
public StringNFactory(int n)
{
this.n=n;
}
public StringN create(String s)
{
return new StringN(this.n, s);
}
}
As the name suggests, a "type parameter" is a type. Not 'a length of a string'.
To be specific: One can imagine the concept of the type fixed length string, and one can imagine this concept has a parameter, whose type is int; one could have FixedString<5> myID = "HELLO"; and that would compile, but FixedString<5> myID = "GOODBYE"; would be an error, hopefully a compile-time one.
Java does not support this concept whatsoever. If that's what you're looking for, hack it together; you can of course make this work with code, but it means all the errors and checking occurs at runtime, nothing special would occur at compile time.
Instead, generics are to give types the ability to parameterize themselves, but only with a type. If you want to convey the notion of 'A List... but not just any list, nono, a list that stores Strings' - you can do that, that's what generics are for. That concept applies only to types and not to anything else though (such as lengths).
Furthermore, javac will be taking care of applying the parameter. So you can't hack it together by making some faux hierarchy such as:
public interface ListSize {}
public interface ListIsSizeOne implements ListSize {}
public interface ListIsSizeTwo implements ListSize {}
public interface ListIsSizeThree implements ListSize {}
and then having a FixedSizeList<T extends ListSize> so that someone can declare: FixedSizeList<ListIsSizeTwo> list = List.of(a, b);.
The reason that can't work is: You can't tell javac what to do, it's not a pluggable system. Java 'knows' how to apply type bounds. It wouldn't know how to enforce size limits, so you can't do this.
I'm answering the question myself, because the useful information is distributed over several comments/answers. I made this a community-wiki answer, so that I don't earn reputation for suggestions of others.
The feature I'm looking for is apparently a particular case of so-called dependent-typing (thanks #DylanSp). Also template parameters of C++ (with the parameter not being a type) are an example of such a feature (thanks #Turing85). All answers agree that this feature unfortunately does not exist in Java, neither within the syntax of Java Generics (#rzwitserloot and others pointed out that Java specification allows only reference types in the diamond <>), nor any other syntax.
One certainly can manually define types in Java for each particular n. So for my example in my question, one can define classes String1, String2, String3, ..., but only finitely many ones. In order to make the definition of each particular type as simple as possible, one can use an approach with an abstract base class that is shared by all of these classes, see #meriton's nice suggestion.
Not what I was thinking of, but with finitely many cases also a code generator (mentioned by #Hulk) should be an option. If I understand correctly that's also what #MC Emperor had in mind when mentioning annotations.
However, if one really wants to stick to infinitely many classes (that's what I want), the only way out seems to be, to make the counter n a member of a single class and just think of them being different types. At compiler-level, there won't be any type-checking, so one has to implement type-safety oneself. The suggestion with the factory made by #Little Santi would be a way to bring more structure into this approach.
This question already has answers here:
why we can't override a method and define it to return a superclass of the original method?
(3 answers)
Closed 3 years ago.
PLEASE NOTE - I am asking WHY? It would be very useful if you could give an example where changing the return type actually breaks the code
why can't I change the return type of an overridden method (other than covariant return types).
class Parent{
public void sayhello(){ ... };
}
class Child extends Parent{
public String sayhello() { . . .}
}
Now if I run the following code:
class test{
public static void main(String[] args){
Parent p = new Child();
p.sayHello();
}
}
Cam someone please confirm if the following steps are happening:
Compiler finds out the type of object 'p' which is Parent.
Compiler checks if method 'sayHello()' is present in Parent class.
During Runtime, JVM finds out that it is a Child object and calls child version of the method.
Child method is called.
Thanks.
Let's use a simple example to explain why it doesn't make any sense to change the return type of an overridden method.
Suppose I have a Car object:
class Car {
public String getModel() {
return "Awesome Car";
}
}
This Car class has a method getModel() that returns a String.
Now, you have another class that extends Car and overrides the getModel() method:
class DumbCar extends Car {
#Override
public Hamburger getModel() {
return new Hamburger();
}
}
Suddenly, you have a major problem. You create a DumbCar object and since you know that all Car objects can tell you their model, you try to get that model:
DumbCar myCar = new DumbCar();
System.out.println(myCar.getModel());
The output is A juicy Big Mac!
Does that make any sense to you? You cannot drive a Big Mac.
Java is a heavily type-safe language. When you write a statement asking to getModel(), you need to be absolutely, 100% positive, that the data you get back is what you were expecting. Java enforces that expectation.
Java is a statically typed language.
This means that the compiler checks that all types make sense before the program is even run. So you will not get errors at runtime because some method or field is not "there".
In order for this to work if you have code that says
MyBean x = something.getMyBean();
it cannot be allowed for a subclass of what the compiler has determined the type of something to be to change the return type of getMyBean() to something other than MyBean (subclasses of MyBean are also allowed, this is called narrowing the return type -- but even that was not possible before Java 5).
The problem is basically that such a thing would make the Java type system unsound, and since Java has a statically-typed system this cannot be allowed.
Suppose you had an Expression interface:
interface Expression {
Integer evaluate();
}
And now you have an Addition implementation:
class Addition implements Expression {
private Expression left;
private Expression right;
Addition(Expression left, Expression right) {
this.left = left;
this.right = right;
}
#Override
public Integer evaluate() {
return left.evaluate() + right.evaluate();
}
}
This works as long as expressions evaluate to Integers, e.g.
class Constant implements Expression {
private Integer value;
Constant(Integer value) {
this.value = value;
}
#Override
public Integer evaluate() {
return this.value;
}
}
Which allows us to do things like:
Expression left = new Constant(1);
Expression right = new Constant(2);
Expression addition = new Addition(left, right);
Integer result = addition.evaluate();
What would happen now if you had an expression that instead of evaluating to an Integer evaluated to something else that is not an expression, like a Cat or a Dog?
It would immediately break the soundness of every other expression you had written in the past like that of the last example or the obvious assumptions we made in the Addition.evaluate method where we assumed that left and right expressions returned Integer not Cats or Dogs.
This is probably a really silly question, but I figured I'd try asking! Basically the question is this: can I use a switch statement in my setters?
Here's what I'm doing: I've created a Monster class with the attributes of healthPoints, description, damage, and type, in which type is an enum. In my driver, I've written a few lines of code that will randomly generate a monster type from the enum values, and it will create a new monster using that type.
The issue is that when I display the monster stats, it shows healthPoints and damage as 0, and description as null. When I try to run the program using the default Monster constructor, it crashes and shows a NullPointerException error.
I think the error is in either my setters or the constructors. Each of the setters has a switch case inside, which sets a certain number of healthPoints, damage, and a specific description depending on the type of monster that is randomly generated. I'm not sure if it's even okay to use a switch statement in a setter because I've never really had to before.
Here is some of my code. First, my constructors.
public Monster(int healthPoints, monsterType type, int damage, String description)
{
setHealthPoints(healthPoints);
setType(type);
setDamage(damage);
setDescription(description);
}
public Monster(monsterType type)
{
setType(type);
}
Below is one of the setters.
public void setHealthPoints(int healthPoints)
{
switch(type)
{
case DROW:
healthPoints = 30;
break;
case LICH:
healthPoints = 40;
break;
case ORC:
healthPoints = 20;
break;
case OWLBEAR:
healthPoints = 20;
break;
case RUST_MONSTER:
healthPoints = 10;
break;
}
this.healthPoints = healthPoints;
}
And below is the line in the driver that creates the monster.
int number = new Random().nextInt(monsterType.values().length);
Monster monster = new Monster(monsterType.values ()[number]);
I want the monster's healthPoints, damage, and description to be set depending on the type of monster that is generated. I'm not sure if this can be done using switches in the setters, and I feel like my mistake is probably obvious and I'm just not seeing it because I've been staring at it for a long time...or, if there's a different or easier way to do this, please let me know! Thank you for taking the time to read; I tried to make my question/problems thorough.
Explanation
Yes you can use switch cases in the constructor and in methods. The issue is that you are calling setHealthPoints before setType but the first methods uses the type in its switch statement:
setHealthPoints(healthPoints);
setType(type);
Therefore the variable type is uninitialized when you visit the switch of setHealthPoints and thus currently null.
A switch statement will throw a NullPointerException if its argument is null. Thus you get the NPE.
You can fix this by first executing setType.
Advice on game architecture
You should create a more readable structure that is also easier to maintain and especially easy to extend.
Therefore consider creating a Monster interface or abstract class. After that create explicit monsters as subclasses. The class Monster will contain everything that is equal among all monsters, try to abstract from specific monsters. The monsters themselve will then only contain what differentiates them from the rest.
For example you could use some kind of this:
public abstract class Monster {
private int mHealthPoints;
private int mDamage;
private String mDescription;
public Monster(int healthPoints, int damage, String description) {
this.mHealthPoints = healthPoints;
this.mDamage = damage;
this.mDescription = description;
}
public int getHealthPoints() {
return this.mHealthPoints;
}
public int getDamage() {
return this.mDamage;
}
public String getDescription() {
return this.mDescription;
}
}
And then you have specific monster classes like
public class Orc extends Monster {
private static int HEALTH = 20;
private static int DAMAGE = 10;
private static String DESCRIPTION = "Nasty orc.";
public Orc() {
super(Orc.HEALTH, Orc.DAMAGE, Orc.DESCRIPTION);
}
}
You also don't need that monsterType enum anymore since you can differentiate via monster instanceof Orc. However for a really modular design you shouldn't work on specific classes. Instead use a lot of interfaces that describe properties and abilities.
For example interfaces like:
CanAttack
IsAttackable
HasHealth
CanWalk
CanFly
CanCollide
...
Your game logic then may be built only on those interfaces. For example CanAttack could look like:
public interface CanAttack {
void attack(IsAttackable target);
}
The huge advantage is that you then can easily extend your game. For example by creating monsters with arbitrary combinations of abilities:
public SuperFlyingPig extends Monster implements
CanAttack, HasHealth, CanWalk, CanFly {
...
}
And it magically will work without any additional coding effort since your logic will not care for the specific monster. It will only work on the interfaces.
so I'm making a card game.
I have an arrayList of type player in my main, and I have filled it with a certain type of player (in this case, a good player - good player being a subclass of player with a couple of additional methods)
I may just be brain dead from looking at this for too long however heres my code.
I create 4 good players - again - goodPlayer extends player
ArrayList<Player> players = new ArrayList<>();
for (int i = 0; i < 4; i++)
{
goodPlayer gp = new goodPlayer();
players.add(gp);
}
Then after a couple of methods are called I want to call a method from goodPlayer called count, on each of the goodPlayers in players arrayList
for (Player p : players)
{
count(p.getHand());
}
what am I doing wrong as what I currently have is incorrect, unless I make count static and call goodPlayer.count however I do not want the class to act in a static context.
Thanks.
Why don't you use type casting? You don't need to make count method a static one to use it. If you are 100% sure that all players in that list will be from GoodPlayer class (by convention, class names start with a capital), then just cast it as
((GoodPlayer) player).count();
If you're not sure if all players will be from the class GoodPlayer, then add a test to avoid a runtime error:
if (player instanceof GoodPlayer) {
((GoodPlayer) player).count();
}
A solution would be to add a method on your abstract player class and implement that differently in your derived types of player. For example:
public abstract class Player
{
public abstract int count();
}
public class GoodPlayer extends Player
{
public int count()
{
return totalHandsPlayed * 3; //Or something meaningful;
}
}
This example is showing the you have a base class that asks any inherited classes to implement the methods defined as abstract. (in this case count).
If your players list is always going to hold GoodPlayer objects, then it would be best to have it as a a list of GoodPlayers
Otherwise, the answer given by user mehmetakcay best suits your purpose.
though the information you have provided seems to be little bit abstract but you can do in following manner (using downcasting, though its not recommended):
for(Player p:players){
GoodPlayer gp=(GoodPlayer)p;
gp.count();
}
I am doing astrophysical research. I wrote a package containing the classes Star, Band, and Datfile. I also have the enumerated type of BandName. Each star contains several Bands, each Band contains several Datfiles.
I have observational data for several galaxies. For each of these, I make a StarDatabase class (a HashMap of Stars) and a Main class.
The problem I'm having is with the enumerated type of BandName. So far, all of the data I have used has been in the I and V bands. Now I have data in J, H, and K bands. If I simply add J, H, and K to BandName, all of my loops that iterate over all of the items in BandName and do something are now broken.
Any ideas?
Edit: To sum up my problem, I want every package to have its own BandName enum that it can iterate through. But this doesn't work, because the methods in the Star package are expecting objects of type Star.BandName and I am providing objects of type IndividualPackage.BandName.
You can't inherit an enum from another enum, although you can have your enum implement an interface. The technical problem (that all enums implicitly extend java.lang.Enum, thus they can't extend another class, only implement additional interfaces) is no accident:
For the most part, extensibility of enums turns out to
be a bad idea. It is confusing that elements of an extension type are instances of
the base type and not vice versa. There is no good way to enumerate over all of the
elements of a base type and its extension. Finally, extensibility would complicate
many aspects of the design and implementation.
From Effective Java 2nd Edition, Item 34.
However, I don't fully understand your problem: haven't you used values() for iterating through your enum? Then you shouldn't worry about extending your enum with new values.
Please specify more clearly what "broken" is supposed to mean.
Update: so you need to have distinct sets of bands for different types of stars - this can be implemented using distinct enums extending a common interface, e.g.:
interface Band {
String getName();
void doStuff();
...
}
enum BandsVI implements Band {
V, I;
public String getName() { return toString(); }
public void doStuff() { /* do stuff as appropriate for these bands */ }
...
}
enum BandsJHK implements Band {
J, H, K;
public String getName() { return toString(); }
public void doStuff() { /* do stuff as appropriate for these bands */ }
...
}
And you can use these by making your Star class generic:
class Star<T extends Enum<T> & Band> {
private Class<T> bandType;
public Star(Class<T> bandType) { this.bandType = bandType; }
public void printBandNames() {
for (Band b : bandType.getEnumConstants())
System.out.println(b.getName());
}
public void doStuffOnAllBands() {
for (Band b : bandType.getEnumConstants())
b.doStuff();
}
}
...
Star<BandsVI> star1 = new Star<BandsVI>(BandsVI.class);
Star<BandsJHK> star2 = new Star<BandsJHK>(BandsJHK.class);
star1.printBandNames(); // prints V I
star2.printBandNames(); // prints J H K
This works nicely if the bands are organized into distinct groups. If there are stars with mixed band groups, however, you might prefer an alternative approach:
class Star {
private List<? extends Band> bandTypes;
public Star(List<? extends Band> bandTypes) { this.bandTypes = bandTypes; }
public void printBandNames() {
for (Band b : bandTypes)
System.out.println(b.getName());
}
...
}
...
Star star1 = new Star(Arrays.asList(BandsVI.values()));
Star star3 = new Star(Arrays.asList(new Band[]{BandsVI.V, BandsVI.I, BandsJHK.K}));
...
This allows you to set up stars with an arbitrary mix of bands. However, this way you can't use EnumSet or EnumMap on the bands.
All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance an enum cannot extend anything else. - http://download.oracle.com/javase/tutorial/java/javaOO/enum.html
This is what I'd do (pseudo-code):
class Band
{
String name;
};
static Band J("J");
static Band K("K");
static ArrayList<Band> JK;
static ArrayList<Band> IHL;
class Star
{
Star(ArrayList<Band> bands)
}
This way you can add bands by just creating more Band objects. Each start has the list of bands it uses so it can iterate over all them.