I am trying to create a class called GameCharacter which represents a game character and has the following properties: • name (a String) • powers (a set of Power objects)
How do I set up an object like this, within a class? The constructor must use varargs as Powers can have different parameters. This is my attempt, but clearly is does not work! This is from a past 2019 assignment question that I don't have solutions for, and I'm a beginner at java. Any help understanding this is appreciated!
class GameCharacter{
private String name;
private int cost;
class Powers{
public Powers(Power... powers) {
for (int i: powers) {
this.i = powers
}
}
}
//constructor
public GameCharacter(String name, int cost, Power... powers) {
this.name = name;
}
}
Power
objects, use Set < Power >
If you want to track a set of Power
objects, use the Set
class. No need for varargs, just pass a Set
object.
To make this very simple, let's use the Record
feature coming to Java 16. The compiler implicitly creates the constructor, getters, equals
& hashCode
, and toString
. A record is appropriate when the main purpose of the class is to transparently and immutably carry data. Using records makes this example code much shorter.
package work.basil.example.Game;
import java.util.Set;
public record GameCharacter (String name, Set <Power> powers)
{
}
For an unmodifiable Set
, use the Set.of
methods.
package work.basil.example.Game;
public record Power(String description)
{
}
package work.basil.example.Game;
import java.util.Set;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
GameCharacter susan = new GameCharacter(
"Susan" ,
Set.of(
new Power( "invisibility" ) ,
new Power( "force field" )
)
);
GameCharacter ben = new GameCharacter(
"Ben" ,
Set.of(
new Power( "physical strength" ) ,
new Power( "rock-like hide" )
)
);
System.out.println( "susan = " + susan );
System.out.println( "ben = " + ben );
}
}
When run:
susan = GameCharacter[name=Susan, powers=[Power[description=force field], Power[description=invisibility]]]
ben = GameCharacter[name=Ben, powers=[Power[description=rock-like hide], Power[description=physical strength]]]
If you were to define conventional classes rather than record
, you would define two member fields on your GameCharacter
class: String
for name, and Set < Power > powers
for the collection of Power
objects. And you would add a constructor taking two arguments for those two fields.
package work.basil.example.Game;
import java.util.Objects;
import java.util.Set;
public final class GameCharacter
{
// Member fields
private final String name;
private final Set < Power > powers;
// Constructor
public GameCharacter ( String name , Set < Power > powers )
{
this.name = name;
this.powers = powers;
}
public String name ( ) { return name; }
public Set < Power > powers ( ) { return powers; }
@Override
public boolean equals ( Object obj )
{
if ( obj == this ) return true;
if ( obj == null || obj.getClass() != this.getClass() ) return false;
var that = ( GameCharacter ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.powers , that.powers );
}
@Override
public int hashCode ( )
{
return Objects.hash( name , powers );
}
@Override
public String toString ( )
{
return "GameCharacter[" +
"name=" + name + ", " +
"powers=" + powers + ']';
}
}
Now we can get back to your specific Question: how to handle varargs in your constructor.
Here we add a second constructor to the class seen above. This second one takes a varargs of type Power
.
The varargs arrives as an array of Power
objects. We can loop the elements of that array with the for-each syntax. We add each element to a new Set
implementation of our choice, in this case the HashSet
class.
public GameCharacter ( String name , Power... powers )
{
this.name = name;
Set<Power> s = new HashSet <>(); // New empty `Set` implementation.
for ( Power power : powers ) // `powers` is the varargs array of `Power` objects.
{
s.add(power); // Add each element of array, each `Power` object, to our `Set` collection.
}
this.powers = s;
}
To use this constructor, we tweak our App
class to drop the calls to Set.of
.
package work.basil.example.Game;
import java.util.Set;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
GameCharacter susan = new GameCharacter(
"Susan" ,
new Power( "invisibility" ) ,
new Power( "force field" )
);
GameCharacter ben = new GameCharacter(
"Ben" ,
new Power( "physical strength" ) ,
new Power( "rock-like hide" )
);
System.out.println( "susan = " + susan );
System.out.println( "ben = " + ben );
}
}
Here is the source code for the entire class of our revised GameCharacter
, now offering two constructors.
package work.basil.example.Game;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public final class GameCharacter
{
// Member fields
private final String name;
private final Set < Power > powers;
// Constructors
// First constructor
public GameCharacter ( String name , Set < Power > powers )
{
this.name = name;
this.powers = powers;
}
// Second constructor
public GameCharacter ( String name , Power... powers )
{
this.name = name;
Set<Power> s = new HashSet <>();
for ( Power power : powers )
{
s.add(power);
}
this.powers = s;
}
// Getters
public String name ( ) { return name; }
public Set < Power > powers ( ) { return powers; }
// `Object` overrides
@Override
public boolean equals ( Object obj )
{
if ( obj == this ) return true;
if ( obj == null || obj.getClass() != this.getClass() ) return false;
var that = ( GameCharacter ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.powers , that.powers );
}
@Override
public int hashCode ( )
{
return Objects.hash( name , powers );
}
@Override
public String toString ( )
{
return "GameCharacter[" +
"name=" + name + ", " +
"powers=" + powers + ']';
}
}
Tip: If you want your member field powers
to be an unmodifiable set, you can reduce that second constructor to this:
// Second constructor
public GameCharacter ( String name , Power... powers )
{
this.name = name;
this.powers = Set.of( powers ) ; // Make an unmodifiable set from the varargs array.
}