Object-Oriented design - Spells - java

I work on my first Java project, which is a basic roleplaying game. Now I work on spells, and I need some OOD guidance.
I have Character, which is an abstract class. Character has some subclasses (like mage, fighter, rogue, cleric).
Mage and cleric(as for now, cleric doesn't have mana, but it might change) are both spell-casters.
I also have a Spell class, with some info (like spell name, mana cost etc). MageSpellsList and ClericSpellsList are another classes and both have lists of class Spell. and I also have Effects class(casting a spell should use it).
What would be a good object oriented design for dealing with spells (the solution shouldn't include Effects class, I can deal with that later) ?
Maybe using a "SpellCaster" interface with some methods like castSpell and showSpellbook, so Mage and Cleric will implement the interface? .
Maybe MageSpellsList and ClericSpellsList should be a subclass of Spell ? My goal is to use castSpell("spell name here") and let castSpell do the job, by using a good OOD, rather than writing a specific method for each spell (and without duplicate code between mage and Cleric)
Mage.java:
public class Mage extends Character {
private List<Spell> spellBook;
private int mana;
private int CurrentMana;
public Mage(String name) {
super(name);
setName(name);
setCharacterClass("Mage");
setLevel(1);
setHitDice(4);
setStrength(10);
setConstitution(10);
setDexterity(14);
setIntelligence(16);
setWisdom(14);
setCharisma(10);
setHp((int) (4 + getModifier(getConstitution())));
setCurrentHp(getHp());
setArmorClass(10 + getModifier(getDexterity()));
setBaseAttackBonus(0);
setMana(20 + 2 * getModifier(getIntelligence()));
setCurrentMana(getMana());
spellBook = new ArrayList<Spell>();
}
public int getMana() {
return mana;
}
public int getCurrentMana() {
return CurrentMana;
}
protected void setMana(int mna) {
mana = mna;
}
protected void setCurrentMana(int CurrMana) {
CurrentMana = CurrMana;
}
public void showSpellBook() {
for (Iterator<Spell> iter = spellBook.iterator(); iter.hasNext(); ) {
Spell spell = iter.next();
System.out.println("Spell name: " + spell.getSpellName());
System.out.println("Spell effect: " + spell.getEffect());
}
}
public void addToSpellBook(String spellName) {
Spell newSpell;
newSpell = MageSpellsList.getSpell(spellName);
spellBook.add(newSpell);
System.out.println(newSpell.getSpellName() + " has been added to the spellbook");
}
public void chooseSpells() {
System.out.println();
}
void castSpell(String spellName, Character hero, Character target) {
try {
Spell spell = MageSpellsList.getSpell(spellName);
System.out.println("You casted: " + spellName);
System.out.println("Spell effect: " + spell.getEffect());
} catch (Exception e) {
System.out.println("No such spell");
}
}
}
Spell.java:
public class Spell {
private String name;
private int spellLevel;
private String effect;
private int manaCost;
private int duration;
Spell(String name, int spellLevel, String effect, int manaCost, int duration) {
this.name = name;
this.spellLevel = spellLevel;
this.effect = effect;
this.manaCost = manaCost;
this.duration= duration;
}
String getSpellName() { return name; }
int getSpellLevel() { return spellLevel; }
String getEffect() { return effect; }
int getManaCost() {
return manaCost;
}
int getDuration() { return duration; }
}
MageSpellsList.java:
public class MageSpellsList {
static List<Spell> MageSpellsList = new ArrayList<Spell>();
static {
MageSpellsList.add(new Spell("Magic Missiles", 1, "damage", 2, 0));
MageSpellsList.add(new Spell("Magic Armor", 1, "changeStat", 2, 0));
MageSpellsList.add(new Spell("Scorching Ray ", 2, "damage", 4, 0));
MageSpellsList.add(new Spell("Fireball", 3, "damage", 5,0 ));
MageSpellsList.add(new Spell("Ice Storm", 4, "damage", 8, 0));
}
static void showSpellsOfLevel(int spellLevel) {
try {
for (Iterator<Spell> iter = MageSpellsList.iterator(); iter.hasNext(); ) {
Spell spell = iter.next();
if (spellLevel == spell.getSpellLevel()) {
System.out.println("Spell name: " + spell.getSpellName());
System.out.println("Spell effect: " + spell.getEffect());
}
}
} catch (Exception e){
System.out.println("Epells of level " + spellLevel + " haven't been found in spells-list");
}
}
static Spell getSpell(String spellName) {
try {
for (Iterator<Spell> iter = MageSpellsList.iterator(); iter.hasNext(); ) {
Spell spell = iter.next();
if (spellName.equals(spell.getSpellName())) {
return spell;
}
}
} catch (Exception e){
System.out.println(spellName + " haven't been found in spells-list");
return null;
}
return null;
}
}
Effects.java:
public class Effects {
public void damage(int dice, Character attacker, Character target){
int damage = DiceRoller.roll(dice);
System.out.println(attacker.getName() + " dealt " + damage + " damage to " + target.getName());
target.setCurrentHp(target.getCurrentHp() - damage);
}
public static void damage(int n, int dice, int bonus, Character target) {
int damage = DiceRoller.roll(n,dice,bonus);
System.out.println("You dealt" + damage + "damage to " + target.getName());
target.setCurrentHp(target.getCurrentHp() - damage);
}
public static void heal(int n, int dice, int bonus, Character target) {
int heal = DiceRoller.roll(n,dice,bonus);
if (heal + target.getCurrentHp() >= target.getHp()) {
target.setCurrentHp(target.getHp());
} else {
target.setCurrentHp(target.getCurrentHp() + heal);
}
System.out.println("You healed" + heal + " hit points!");
}
public static void changeStat(String stat, int mod, Character target){
System.out.println(stat + " + " + mod);
switch (stat) {
case "strength":
target.setStrength(target.getStrength() + mod);
break;
case "constitution":
target.setConstitution(target.getConstitution() + mod);
break;
case "dexterity":
target.setDexterity(target.getDexterity() + mod);
break;
case "intelligence":
target.setIntelligence(target.getIntelligence() + mod);
break;
case "wisdom":
target.setWisdom(target.getWisdom() + mod);
break;
case "charisma":
target.setCharisma(target.getCharisma() + mod);
break;
case "armorClass":
target.setArmorClass(target.getArmorClass() + mod);
break;
}
}
}

Preamble
I try to generalise the classes as much as possible, so I do not end up with lots of specific classes that just represent different data, instead of a different structure. Also, I try to separate data structures from game mechanics. In particular, I try to keep the combat mechanics all in one place, instead of splitting them across different classes, and I try not to hard-code any data. In this answer, we will cover the characters, their abilities/spells, the effects of the abilities, and the combat mechanics.
Characters
Consider, for instance, a PlayableCharacter, that represents your characters. This is a standard data class. It provides methods for increasing or decreasing health and mana, and a collection of available abilities.
class PlayableCharacter {
private final int maxHealth;
private int health;
private final int maxResource; // mana, energy and so on
private int resource;
private final Collection<Ability> abilities;
// getters and setters
}
Abilities
Abilities are equally data classes. They represent mana costs, triggered effects, and so on. I often represent this as a normal class, and then read the individual abilities from external data files. Here we can skip that and declare them with enumerations.
enum Ability {
FIREBALL("Fireball", 3, 5, new Effect[] {
new Effect(Mechanic.DAMAGE, 10, 0),
new Effect(Mechanic.BURN, 2, 3)
});
private final String name;
private final int level;
private final int cost;
private final List<Effect> effects;
}
Effects
Finally the effects tell what an ability does. How much damage, how long it lasts, how it affects a character. Again, this is all data, no game logic.
class Effect {
private final Mechanic effect;
private final int value;
private final int duration;
}
The mechanics are just an enumeration.
enum Mechanic {
DAMAGE, BURN;
}
Mechanics
Now it is time to make things work properly. This is the class that your game loop will be interacting with, and you must feed it the game state (which characters are battling, for instance).
class BattleEngine {
void useAbility(PlayableCharacter source, PlayableCharacter target, Ability ability) {
// ...
}
}
How you implement each mechanic is up to you. It can range from an infernal switch or if/else for each Mechanic, or you can move the code to the Mechanic enum, or to private nested classes and use an EnumMap to retrieve each handler.
Example Mechanic
interface MechanicHandler {
void apply(PlayableCharacter source, PlayableCharacter target, Effect effect);
}
class BattleEngine {
private final Map<Mechanic, MechanicHandler> mechanics;
void useAbility(PlayableCharacter source, PlayableCharacter target, Ability ability) {
source.decreaseResource(ability.getCost());
for (Effect effect: ability.getEffects()) {
MechanicHandler mh = mechanics.get(e.getMechanic());
mh.apply(source, target, effect);
}
}
private static final class DicePerLevel implements MechanicHandler {
#Override
public void apply(PlayableCharacter source, PlayableCharacter target, Effect effect) {
int levels = Math.min(effect.getValue(), source.getLevel());
int damage = 0;
for (int i = 0; i < levels; ++i) {
int roll; // roll a d6 die
damage += roll;
}
target.decreaseHealth(damage);
}
}
}

The SpellCaster spellbook should be a Map<String, Spell> so you can look it up by name when it is cast. The Spell class should define an abstract method for applying the effects to a Character. I don't see the point of a "SpellCaster" interface because the implementation of the castSpell() method is always the same (the behavior is delegated to the Spell itself).
Here is a sample scenario:
Mage fireMage = new Mage("Red Niminim");
fireMage.addSpell(new Fireball());
fireMage.addAttribute(Attribute.RESIST_FIRE);
fireMage.addAttribute(Attribute.WEAK_TO_COLD);
Mage iceMage = new Mage("Blue Niminim");
fireMage.addSpell(new Icestorm());
fireMage.addAttribute(Attribute.RESIST_COLD);
fireMage.addAttribute(Attribute.WEAK_TO_FIRE);
Cleric cleric = new Cleric("Friar Joe");
cleric.addSpell(new Heal());
// battle!
fireMage.castSpell("Fireball", cleric); // 15 damage
fireMage.castSpell("Fireball", iceMage); // 30 damage
fireMage.castSpell("Fireball", fireMage); // 0 damage
iceMage.castSpell("Icestorm", cleric); // 15 damage
iceMage.castSpell("Icestorm", fireMage); // 30 damage
iceMage.castSpell("Icestorm", iceMage); // 0 damage
cleric.castSpell("Heal", cleric); // 15 healed
Attribute.java
public enum Attribute {
RESIST_FIRE, WEAK_TO_FIRE, RESIST_COLD, WEAK_TO_COLD;
}
Spell.java
public abstract class Spell {
private String name;
private int manaCost;
public Spell(String name, int manaCost) {
this.name = name;
this.manaCost = manaCost;
}
public abstract void apply(Character character);
public String getName() {
return name;
}
public int getManaCost() {
return manaCost;
}
}
SpellCaster.java (snippet)
public void castSpell(String name, Character character) {
getSpellBook().get(name).apply(character);
}
public void addSpell(Spell spell) {
getSpellBook().put(spell.getName(), spell);
}
Fireball.java
public class Fireball extends Spell {
private static final String NAME = "Fireball";
private static final int MANA_COST = 8;
private static final int DAMAGE_AMOUNT = 15;
public Fireball() {
super(NAME, MANA_COST);
}
#Override
public void apply(Character character) {
int damage = DAMAGE_AMOUNT;
if (character.getAttributes().contains(Attribute.RESIST_FIRE)) {
damage = 0;
}
else if (character.getAttributes().contains(Attribute.WEAK_TO_FIRE)) {
damage = damage * 2;
}
character.setCurrentHp(character.getCurrentHp() - damage);
}
}
Icestorm.java
public class Icestorm extends Spell {
private static final String NAME = "Icestorm";
private static final int MANA_COST = 8;
private static final int DAMAGE_AMOUNT = 15;
public Icestorm() {
super(NAME, MANA_COST);
}
#Override
public void apply(Character character) {
int damage = DAMAGE_AMOUNT;
if (character.getAttributes().contains(Attribute.RESIST_COLD)) {
damage = 0;
}
else if (character.getAttributes().contains(Attribute.WEAK_TO_COLD)) {
damage = damage * 2;
}
character.setCurrentHp(character.getCurrentHp() - damage);
}
}
Heal.java
public class Heal extends Spell {
private static final String NAME = "Heal";
private static final int MANA_COST = 10;
private static final int HEAL_AMOUNT = 15;
public Heal() {
super(NAME, MANA_COST);
}
#Override
public void apply(Character character) {
character.setCurrentHp(character.getCurrentHp() + HEAL_AMOUNT);
}
}

Here is an example of how you can use enum instead of strings in your Effects class. I took the liberty of renaming your Character class to PlayerCharacter to avoid collision with java.lang.Character.
Effects.java:
public class Effects {
...
public static void changeStat(Stat status, int mod, PlayerCharacter target) {
System.out.println(status + " + " + mod);
status.effect(mod).accept(target);
}
}
A little bit cleaner, isn't it? How it works? The magic is all in the enum:
Stat.java:
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
import java.util.function.ObjIntConsumer;
import java.util.function.ToIntFunction;
public enum Stat {
STRENGTH(PlayerCharacter::getStrength, PlayerCharacter::setStrength),
CONSTITUTION(PlayerCharacter::getConstitution, PlayerCharacter::setStrength),
DEXTERITY(PlayerCharacter::getDexterity, PlayerCharacter::setDexterity),
INTELLIGENCE(PlayerCharacter::getIntelligence, PlayerCharacter::setIntelligence),
WISDOM(PlayerCharacter::getWisdom, PlayerCharacter::setWisdom),
CHARISMA(PlayerCharacter::getCharisma, PlayerCharacter::setCharisma),
ARMORCLASS(PlayerCharacter::getArmorClass, PlayerCharacter::setArmorClass);
Stat(ToIntFunction<PlayerCharacter> findcurrentvalue, ObjIntConsumer<PlayerCharacter> setnewvalue) {
this.findcurrentvalue = findcurrentvalue;
this.setnewvalue = setnewvalue;
}
private ToIntFunction<PlayerCharacter> findcurrentvalue;
private ObjIntConsumer<PlayerCharacter> setnewvalue;
Consumer<PlayerCharacter> effect(int mod) {
return target -> {
setnewvalue.accept(target, findcurrentvalue.applyAsInt(target) + mod);
};
}
}
The two mysterious types ToIntFunction and ObjIntConsumer are functional interfaces:
ToIntFunction takes some kind of object as input (here: a PlayerCharacter) and returns an int.
ObjIntConsumer takes some kind of object (here: a PlayerCharacter) and an int as input, and returns nothing.
You could also create your own functional interface if you like, like so:
Effect.java:
#FunctionalInterface
public interface Effect<T extends PlayerCharacter> {
void affect(T t);
}
Stat.java:
...
Effect<PlayerCharacter> effect(IntUnaryOperator calculator) {
return target -> {
setnewvalue.accept(target, calculator.applyAsInt(findcurrentvalue.applyAsInt(target)));
};
}
...
Then you can do this in changeStat:
public class Effects {
...
public static void changeStat(Stat status, int mod, PlayerCharacter target) {
System.out.println(status + " + " + mod);
status.effect(x -> x + mod).affect(target);
}
}
This way you can decide in the Effects class what will happen. Well, I don't imagine the character stats to change much from spells, but a similar mechanic can be used for HP and such :)
The x -> x + mod bit could come from the spell itself too. It's a function that takes an int and returns an int, which is called an IntUnaryOperator in Java:
Effects.java:
...
public static void boost(int dice, PlayerCharacter target) {
int value = DiceRoller.roll(dice);
changeStat(Stat.STRENGTH, x -> x + value, target);
}
public static void changeStat(Stat status, IntUnaryOperator change, PlayerCharacter target) {
status.effect(change).affect(target);
}
...
Here the spell (boost in this case, which I just invented!) will increase the player's strength (the STRENGTH constant) by a dice roll. It accomplishes this by calling the changeStat with three parameters:
STRENGTH → tells the method what status to change.
A "formula" for changing the value (note that you don't actually need to know the value here, just the formula!).
The target to affect.
As you can see, there is no need here to know how to find the strength value, or how to set it to something else. That is all handled by the enum, so you can keep your spell code clean.
You could even inline the changeStat method directly in the spell method this way, since there isn't really any "real" code in it anymore – that logic is hidden in the enum.
Clean and neat :)

I think your idea of having a SpellCaster interface (which includes the castSpell()) is a good one. This defines the behavior or ability of the character.
I would include the list of available spells as an instance field in the Mage or Cleric classes. Come to think of it, maybe it would be a good idea to create an abstract class called SpellCaster which extends Character. The SpellCaster class can declare the list of spells and subclasses (Mage and Cleric) can add specific spells to it.
I'm going to discard the Effects class for now. Each spell can take care of its own behavior. So for example, when calling castSpell("spellName", hero, target) you can pass the required parameters to the spell object and it can take care of dealing the damage or changing stats.
In addition, there could be multiple Spell subclasses. For example, DamageSpell, Buff, Debuff. The superclass Spell has a method apply() and each subclass can implement it with it's own behavior. When calling castSpell() then you delegate the control to a specific subclass of a Spell which has encapsulated the behavior and knows exactly if it should deal damage or change stats. That's essentially the Strategy Pattern.

Why treat spells different than abilities? A fighter class might not have spells as magic spells, but it should be able to perform class specific moves like a whirlwind.
Class PlayableCharacter: abstract class, defines the abstract methods for handling resources(regen rate, max, effects on character), abilities, gear. And implements all the basics.
Class ManaCharacter: extends PlayableCharacter handles it resource as mana.
Class Mage extends ManaCharacter: Will just implement the methods to define what kind of gear it can use, the special abilities it can perform, etc.

Related

Java - incorrect creation of subclasses and super classes

First, I think the title of this post could be better, so if you want to edit it feel free to do so (or let me know how you think I should edit it).
I am going over practice problems for Java interviews. I am not interviewing right now, but I think this is the best way for me to find all my weak spots with Java. And before you say it, yes, I am finding I am VERY weak in many areas of Java and that I will need to do lots or review before interviewing.
I have some questions about the following code:
public class VehicleApp {
public static void main(String[] args) {
Ford myFord = new Ford();
System.out.println(myFord.countWheels());
Kawasaki myKawasaki = new Kawasaki(1985, "Eliminator");
System.out.println(myKawasaki.countWheels());
}
}
class Vehicle {
protected String make;
protected int numWheels;
public Vehicle() { }
public String countWheels() {
return "The number of wheels this " + make + " has is " + numWheels + ".";
}
}
class Ford extends Vehicle {
public Ford() {
make = "Ford";
numWheels = 4;
}
}
class Kawasaki extends Vehicle {
private String model;
private int year;
public Kawasaki(int year, String model) {
make = "Kawasaki";
numWheels = 2;
this.model = model;
this.year = year;
}
public String countWheels() {
return "The number of wheels this " + year + " " + make + " " + model + " has is " + numWheels + ".";
}
}
First, I notice that there are no references to super() in the code. I thought that when you are dealing with super classes and subclasses, it was required that the subclass constructor include a reference to the super class constructor in the form of super(); (and including parameters if the super class constructor has them). Yet this code seems to work without them. Am I wrong about this requirement? Am I missing something else in this picture?
Second, the Kawasaki class doesn't include the decoration #Override for the countWheels() method. Since this method has the same name (albeit different parameters) as the super class' countWheels() method, wouldn't it be required to have an #Override decoration? Or is that only if the parameters are the same type and same order?
Thanks!
If you do not explicitly call super() in your derived class, the Java compiler will automatically generate a call to super() for you. But this, of course, only works if the base class constructor takes no arguments. This can be demonstrated by adding a System.out.println("Constructor called."); statement to your otherwise empty Vehicle constructor.
The #Override decorator, as you have found out but have not convinced yourself of, is optional. But it is considered a "best practice" to use this when overriding a method for catching errors if you change the method signature.
The one, hopefully constructive, comment I would make is that since a Vehicle must have attributes make and numWheels, I personally would require that these be specified in the Vehicle constructor. Now there is no possibility of having a derived class with these attributes undefined.
public class VehicleApp {
public static void main(String[] args) {
Ford myFord = new Ford();
System.out.println(myFord.countWheels());
Kawasaki myKawasaki = new Kawasaki(1985, "Eliminator");
System.out.println(myKawasaki.countWheels());
}
}
class Vehicle {
protected String make;
protected int numWheels;
public Vehicle(String make, int numWheels) {
this.make = make;
this.numWheels = numWheels;
}
public String countWheels() {
return "The number of wheels this " + make + " has is " + numWheels + ".";
}
}
class Ford extends Vehicle {
public Ford() {
super("Ford", 4);
}
}
class Kawasaki extends Vehicle {
private String model;
private int year;
public Kawasaki(int year, String model) {
super("Kawasaki", 2);
this.model = model;
this.year = year;
}
#Override
public String countWheels() {
return "The number of wheels this " + year + " " + make + " " + model + " has is " + numWheels + ".";
}
}

Wants static but can't support?

I am trying to create a random car generator that also displays info. I thought I had everything until the randomCar portion. It says that
'com.company.Main.this' cannot be referenced from a static context
under the return statements in the switch. Any thought on to where I may be going wrong?
package com.company;
public class Main {
class Car{
private String name;
private boolean engine;
private int cylinders;
private int wheels;
public Car(String name){
this.name = name;
}
public String getName(){
return name;
}
public int getCylinders() {
if(cylinders == 0){
System.out.println("Unknown amount of cylinders");
}
return cylinders;
}
public int getWheels() {
return wheels;
}
public boolean isEngine() {
return engine;
}
}
class Tacoma extends Car{
public Tacoma(String name) {
super("Tacoma");
}
public boolean isEngine(boolean engine) {
return true;
}
public int getCylinders(int cylinders) {
return 6;
}
public int getWheels(int wheels) {
return 4;
}
}
class Motorcycle extends Car{
public Motorcycle(String name) {
super("Harley Davidson");
}
public boolean isEngine(boolean engine) {
return true;
}
public int getCylinders(int cylinders) {
return 2;
}
public int getWheels(int wheels) {
return 2;
}
}
class Volvo extends Car{
public Volvo(String name) {
super("Volvo");
}
public boolean isEngine(boolean engine) {
return true;
}
public int getCylinders(int cylinders) {
return 4;
}
public int getWheels(int wheels) {
return 4;
}
}
public static void main(String[] args) {
for (int i = 1; i<6; i++){
Car car = randomCar();
System.out.println("Car # " + i + ":" + car.getName() + "\n" +
"Number of cylinders: " + car.getCylinders() + "\n" +
"Number of wheels: " + car.getWheels()+ "\n" +
"Engine is: " + car.isEngine());
}
}
private static Car randomCar() {
int randomNumber = (int) (Math.random()*5) +1;
System.out.println("Random number generated is: " + randomNumber);
switch (randomNumber){
case 1:
return new Tacoma(); // This is where I am getting an error
case 2:
return new Motorcycle(); // This is where I am getting an error
case 3:
return new Volvo(); // This is where I am getting an error
}
return null;
}
}
I would start by reading here: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html -> actually all the chapters there would be useful for you to read: https://docs.oracle.com/javase/tutorial/java/javaOO/index.html
Strictly speaking, to solve your "cannot be referenced from a static context" you can just make your classes static (Car, Tacoma, Motorcycle, Volvo) static class Car{
From my point of view you don't need nested classes, just create the classes in the same package as your Main class and you should be good to go (feel free to create more packages to better structure your classes)
Also I'm assuming your code is a work in progress because there are multiple issues with it:
methods like this don't make sense public boolean isEngine(boolean engine) {return true;} You receive a parameter that you ignore and you return a constant value: true; What I assume you want to do here is to have different types of cars each with its own predefined characteristics, but for that you should set the values for the attributes in the parent, Car. For this you either define protected setters, make the fields protected, or, best, create constructor which takes all the values
public Car(String name, boolean engine, int cylinders, int wheels) {
this.name = name;
this.engine = engine;
this.cylinders = cylinders;
this.wheels = wheels;
}
and you can have in Tacoma
public Tacoma(String name) {
super(name, true, 6, 4);
}
running your code I got the randomNumber 5 so that returned null and got a NPE, I assume work in progress
in your switch you are calling the default constructor new Tacoma() however that isn't available anymore since you defined a constructor with a parameter, use the available constructor or create the no-arg constructor.
There are other concerns regarding OOP principles so I recommend reading them again, just google "java OOP principles" and then "SOLID"... there are a lot of great resources out there, you just need time and patience and you'll get there!
When you put the Car class definition inside the class definition of Main, you made Car an inner class, so that a Car requires an outer class Main instance. In the static method there is no Main instance, and you can’t create the Car without it.
There is an immediate fix: add keyword static to the Car class:
static class Car {
which means there is no link to the enclosing object.
But there is no benefit here to making this a nested class, it would be better not to put one class definition inside another when you’re starting out.
The inner classes you've defined are instance members, meaning they belong to a specific instance of Main, and thus cannot be referenced from a static context that doesn't have a Main instance. The easiest way to resolve this would be to declare all the inner classes static.
First of all, to solve your error: 'com.company.Main.this' cannot be referenced from a static context, make all the methods static:
static class Car{//code here}
static class Volvo extends Car{//code here}
static class Tacoma extends Car{//code here}
static class Motorcycle extends Car{//code here}
Whenever you see that error, it means one static method is calling a non-static method. Therefore, just make both non-static or both static. The only exception is public static void main(String[] args); which must be static.
After solving the original errors, there is more to debug:
'Volvo(java.lang.String)' in 'com.company.Main.Volvo' cannot be applied to '()'
'Motorcycle(java.lang.String)' in 'com.company.Main.Motorcycle' cannot be applied to '()'
'Tacoma(java.lang.String)' in 'com.company.Main.Tacoma' cannot be applied to '()'
All this means is that your methods Tacoma(), Volvo(), and Motorcycle() require the parameter String name. So all you have to do is give them a name: here, it's
`new Tacoma("cool")`
new Volvo("car")
new Motorcycle("harley davidson")`
Finally, after solving the static and parameter problems, you are getting a NullPointerException, because randomCar() returns null. Your method says Car randomCar(), indicating it will return a Car, but then the return statement was return null;. Therefore, just return a Car - rtn here for our purposes:
private static Car randomCar() {
int randomNumber = (int) (Math.random()*5) +1;
System.out.println("Random number generated is: " + randomNumber);
Car rtn = null;
switch (randomNumber){
case 1:
rtn = new Tacoma("cool"); // This is where I am getting an error
case 2:
rtn = new Motorcycle("harley davidson"); // This is where I am getting an error
case 3:
rtn = new Volvo("car"); // This is where I am getting an error
}
return rtn;
}
This isn't all the debugging your code needs, but it's a start: here's what the system did so far:
Random number generated is: 3
Unknown amount of cylinders
Car # 1:Volvo
Number of cylinders: 0
Number of wheels: 0
Engine is: false
Random number generated is: 5
Hooray!
Did this help?

Method to get an enumvalue with an inputString

I'm studying Enums in Java and I have a question that isn't working well on my code.
I did my Enum with seconds and some names, and, later, I did a method that looks for it inside the Enum class.
The idea is to start a counter (that's why I'm using the integer values on Enum) given the name of the operation.
The code is:
public enum Calculator {
plus(30), minus(21), divide(21), times(30);
public int seconds;
public int getSeconds() {
return seconds;
}
Calculator(int seconds) {
this.seconds = seconds;
}
private String name;
Calculator(String name) {
this.name = name();
}
public static Calculator contains(String name) {
for (Calculator ss : Calculator.values()) {
if (ss.name.equalsIgnoreCase(name)) {
System.out.println(name + " input " + ss.name + " enum");
return ss;
}
}
throw new NullPointerException("Invalid name");
}
}
And I have another class that invokes this method.
The following invoker is:
public static void calcInput(String name) {
try {
Calculator.contains(name);
} catch(NullPointerException e){
System.out.println("Invalid parameter, " + e );
}
The thing is that, any input I use, a right or wrong one, it is answering me a NullPointerException. Where am I commiting the mistake?
Thanks in advance!
I'm going to walk through your code and make some comments. Then I will show you some changes you could make.
public enum Calculator {
// convention is to name enum values as constants: UPPER_CASE_WITH_UNDERSCORES
plus(30), minus(21), divide(21), times(30);
// you should not make instance fields public, but private
// because they should not be accessed directly by any other class (in general)
public int seconds;
// methods, like this 'getter' belong below all constructors
public int getSeconds() {
return seconds;
}
Calculator(int seconds) {
this.seconds = seconds;
}
private String name;
// this constructor is never used by your enum values (plus, minus, divide, times)
// if it were used, the name parameter in the line below this one is never used for anything
Calculator(String name) {
// you are trying to set your name field to the name() of the enum value.
// even though this would work, this is not very useful
// since calling MY_ENUM_VALUE.name() already gives you its name
this.name = name();
}
// a method like 'contains' usually returns a boolean value to indicate if this instance does or does not
// contain the parameter you provided. Having a 'contains' method which returns a Calculdator instance is
// confusing to say the least. A better name (judging from the method implementation) would be 'forName'
// then you could do:
// Calculator bla = Calculator.forName("Minus");
// System.out.println(bla == Calulcator.minus);
// which would print "true"
public static Calculator contains(String name) {
for (Calculator ss : Calculator.values()) {
if (ss.name.equalsIgnoreCase(name)) {
System.out.println(name + " input " + ss.name + " enum");
return ss;
}
}
// Opinions are mixed on whether you should throw NullPointerExceptions from your application code
// I personally choose to throw IllegalArgumentException in cases like this
throw new NullPointerException("Invalid name");
}
}
Here is a new version of the code which might do what you expect. I don't understand what the 'seconds' values have to do with the calculator operations though.
public enum Calculator {
PLUS(30),
MINUS(21),
DIVIDE(21),
TIMES(30);
private int seconds;
Calculator(int seconds) {
this.seconds = seconds;
}
public int getSeconds() {
return seconds;
}
public static Calculator forName(String name) {
return valueOf(name.toUpperCase());
}
}
The app class:
public class App {
public static void main(String[] args) {
Calculator calculator = Calculator.forName("Minus");
System.out.println(calculator + " seconds = " + calculator.getSeconds());
}
}
The output is:
MINUS seconds = 21

UML diagram confusion

Hi guys,this is my first question on StackOverflow
I am kind of new to java and I need to solve this uml diagram .
I got a solution from one of my classmates but I don't think it's correct and I did it my way. My question is which one of the solutions is correct? I know that the type of relation is an association one . Not an inheritance
Her code
class Sensor {
protected int value;
protected String location;
public Sensor() { // default constructor
value = 0;
location = "North-West";
}
public Sensor(int value, String location) { // overridden constructor
this.value = value;
this.location = location;
}
protected int getValue() { // value getter
return value;
}
protected void setValue(int v) { // value setter
this.value = v;
}
protected void displaySenzorInfo() { // display information on the sensor
System.out.println("Temperature is " + value + ", located " + location + ".");
}
}
class Controller extends Sensor {
protected String name;
public Controller(String name) { // overridden constructor
this.name = name;
}
public Controller(String name, int value, String location) { // overridden
// instructor
this.name = name;
super.value = value;
super.location = location;
}
public Controller() { // default constructor, which creates a new Sensor()
//Sensor s = new Sensor();
}
protected void checkTemperature() { // checks temperature of sensor
System.out.println("Temperature of " + name + " is " + super.value + ", located at " + super.location + ".");
}
}
public class E3 {
public static void main(String[] args) {
Controller control = new Controller();
control.displaySenzorInfo();
Controller c = new Controller("Pizza", 30, "North");
c.checkTemperature();
}
}
My code
class Sensor{
int value;
String location;
Sensor(){
value=0;
location="Sibiu";
}
Sensor(int value,String location){
this.value=value;
this.location=location;
}
int getValue(){
return value;
}
void setValue(int v){
this.value=v;
}
void displaySenzorInfo(){
System.out.println("Temperature is " + value + ", located " + location + ".");
}
}
class Controller{
Sensor tempSensor;
String name;
Controller(){
name="Sibiu";
tempSensor=30;
}
Controller (String name,Sensor tempSensor){
this.name=name;
this.tempSensor=tempSensor;
}
void checkTemperature(Sensor tempSensor){
if (tempSensor>=30)
System.out.println("the temperature is too high!");
else
System.out.println("the temp is too low" );
}
}
public class E3{
public static void main(String []args){
Sensor s1=new Sensor();
Controller c1=new Controller();
c1.displaySenzorInfo();
Controller c2=new Controller(30,"Oliver");
}
}
Please guys. If you have some suggestions or if you see any problems in m program tell me. I know that I will have some errors because I didn't work at this exercise in any IDE because I am at work and I don't have any . Thank you!!!
your solution is the correct one. As you mentioned already, it is an association and not an inheritance. You can see how an inheritance looks like on wikipedia: https://en.wikipedia.org/wiki/Class_diagram
Though overall coding (MyCode) for relationship from the given diagram is OK, I have following observations. (Her code) - Inheritance is not correct. Unidirectional association is correct.
If this is diagram is only for exercise purpose its OK, otherwise it will violate data hiding and encourage client classes to violate encapsulation (Using somebody else's data directly)
tempSensor=30;is not correct for data type.
if (tempSensor>=30) is incorrect for data type and even if you correct, it violates encapsulation (works on somebody else's data) as an effect of first violation of making instance variables non-private. classes should work on their own data.
Even if for some reason we accept above violation, checkTemperature(Sensor tempSensor) makes use of fresh instance of Sensor (for every call), which is not the one obtained from association relationship. This method should not have parameter, it should work on this.tempSensor (with accepted data leakage). Ideally this is indication that data and its behavior are getting separated and design needs to be corrected.
In case the diagram can not be changed then just remove the parameter in checkTemperature() and take care of data types as shown above.
But I would suggest change at Design level as follows for better encapsulation.
public class SensorNew {
private static final double UPPER_THRESHOLD = 25;
private static final double LOWER_THRESHOLD = 20;
private String location;
private Controller controller;
public SensorNew(String location, Controller controller) {
this.location = location;
this.controller = controller;
}
public int getCurrentTemp() {
// obtain from sensor hardware
return 10; // Just example
}
private void makePeriodicCheck(){
double currentTemp = getCurrentTemp();
if (currentTemp > UPPER_THRESHOLD){
controller.coolDown();
} else if (currentTemp < LOWER_THRESHOLD){
controller.heatUp();
} else {
controller.stopIfRunning();
}
}
public void displaySenzorInfo() { // replace by toString()
System.out.println("Temperature is " + getCurrentTemp()
+ ", located " + location + ".");
}
}
public class ControllerNew {
private String name;
// Need to maintain the state of Controller
// either by variable or State design pattern (preferred)
public ControllerNew(String name, Sensor tempSensor) {
this.name = name;
}
public void coolDown() {
// action depending upon current state of controller
}
public void heatUp() {
// action depending upon current state of controller
}
public void stopIfRunning() {
// action depending upon current state of controller
}
}
The advantage is that we do not have to provide public getXX() setXX() methods to these classes. Hence it maintains encapsulation.

Price value not being passed using an interface Java Observer Pattern

On my course I am learning the different development patterns and the problem i am stuck with is an implementation of the Observer Pattern [1]: http://www.oodesign.com/observer-pattern.html, and them problem i am having is passing a value from the subject, that has been set using a JUnit test, to the observer to it can buy/sell a number of shares. My main question is: What's the problem i am not seeing? and a secondary question: Would my buying/selling of shares code work? if doesn't work, please don't post a solution to the 2nd as i would like to fix atleast one bit myself.
Interface:
public interface ShareWatcher {
public void updatePrice(double price);}
Subject:
public class Share{
public double price = 1.00;
ArrayList<ShareWatcher> list = new ArrayList<ShareWatcher>();
public double getPrice() {
return price;
}
public boolean addShareWatcher(StockBroker stockBroker) {
boolean result;
if(!list.contains(stockBroker)){
list.add(stockBroker);
result = true;
}else{
result = false;
}
return result;
}
public boolean removeShareWatcher(StockBroker stockBroker) {
boolean result;
if(list.contains(stockBroker)){
list.remove(stockBroker);
result = true;
}else{
result = false;
}
return result;
}
}
Observer:
public class StockBroker implements ShareWatcher{
Share share = new Share();
public int portfolio ;
double price;
double buy, sell;
public int increment;
public StockBroker(double SB_BUY, double SB_SELL, int SB_INCREMENT) {
this.buy = SB_BUY;
this.sell = SB_SELL;
this.increment = SB_INCREMENT;
System.out.println(buy + "" + sell + "" + increment);
}
#Override
public void updatePrice(double price) {
this.price = share.getPrice();
}
public int getPortfolio() {
while (price > 2 && price < 2){
if(price < buy){
portfolio = portfolio + increment;
System.out.println("SB2 " + portfolio);
}else if(price > sell){
portfolio = portfolio - increment;
}
}
return portfolio;
}
}
and not sure if this would be needed on here, if not feel free to edit out, but the JUnit Test:
public void testChangePrice1() {
final Share share = new Share();
final StockBroker stockBroker = new StockBroker(SB_BUY, SB_SELL, SB_INCREMENT);
assertTrue(share.addShareWatcher(stockBroker));
share.setPrice(PRICE5);
final int expectedValue2 = 500;
assertEquals(expectedValue2, stockBroker.getPortfolio());
}
To me it seems that you are not understanding that the observer pattern is basically about a CALLBACK after a certain event.
Meaning: the observers can register themselves somewhere; in your terms, they would be using the addShareWatcher() method of Subject.
Then, when for some reason the price is changed, Subject should iterate its list of ShareWatchers ... and invoke "updatePrice()" on each of the objects.
Side note: of course this is just example code; but keep in mind that you should not use ordinary "double" values that represent currency (see http://www.javapractices.com/topic/TopicAction.do?Id=13 on alternatives).

Categories