Johann MARTINET
Johann MARTINET

Reputation: 94

Set : Prevent insertion of same type

I would like to prevent the insertion of an object in a Set if an object of the same type is already in. Example :

If i have following class :

public class Card{...}
public class Clubs extends Card{...}
public class Diamonds extends Card{...}
public class Hearts extends Card{...}
public class Spades extends Card{...}

The behavior should be :

Set<Card> cards = new HashSet<>();
System.out.println(cards.add(new Diamonds()) //prints "true"
System.out.println(cards.add(new Spades()) //prints "true"
System.out.println(cards.add(new Spades()) //prints "false"

Is there a simple way or a set implementation which can give me this behavior ?

I thought about overriding hashcode/equals for each class extending Card to manage the hashset behavior but I think it is not a reliable solution.

Edit :

I took the example of a basic deck card but each card is not only a "color", the number of fields, type of fields and methods of each class(Clubs,Diamonds,Spades,Hearts) differs.

Edit2

This is a more specific definition of my class Card :

public abstract class Card
{
    /**Keyword of this card*/
    private final String keyword;

    public Card(String keyword)
    {
         this.keyword = keyword; 
    }

    public abstract void cardDefinition();
    ...
}

Here definition of my classes extending card :

public class Diamonds extends Card
{
    /**KEYWORD of this card*/
    public static final String KEYWORD = "cell";
    /**Name of this cell*/
    private final int id;
    /**Universe of this cell*/
    private final Universe universe;
    /**Material name of this cell*/
    private final String materialName;

    public Diamonds(int id, Universe universe, String materialName)
    {
        super(KEYWORD);
        this.id = id;
        this.universe = universe;
        this.materialName = materialName;
        ...
    }
    @Override
    public void cardDefinition(){...}
    public void someMethod(OrientedSurface orientedSurface) {...}
    ...
}


public class Spades extends Card
{
    /**keyword of this card*/
    public static final String KEYWORD = "mat";
    /**Name of this material card*/
    private final String name;
    /**Density of this material card*/
    private final double density;'

    public Spades(String name, double density)
    {
        super(KEYWORD);
        this.name = name;
        this.density = density;
        ...
    }
    @Override
    public void cardDefinition(){...}
    public void someMethodBis(Material material) {...}
    ...
}
...

Upvotes: 0

Views: 55

Answers (3)

Mick Mnemonic
Mick Mnemonic

Reputation: 7956

Based on your updated answer, perhaps using a mapping of keywords to Cards might work for you:

Map<String, Card> cards = new HashMap<>();

This is assuming that keywords are unique, and that only one card per keyword should be in the map. Storing data in the map could then look like this:

Card mySpade = new Spade(...);

if (!cards.contains(Spades.KEYWORD)) {
    cards.put(Spades.KEYWORD, mySpade);
}...

Upvotes: 1

GhostCat
GhostCat

Reputation: 140553

The HashSet implementation uses the equals() respectively hashCode() methods of the objects you insert.

In other words: if you want to make that any Spade is equal to any other Spade object - you will have to @Override equals() and hashCode() accordingly.

Of course - that is a rather wrong design in the first place.

The suits should not be classes but enums. And then you use an EnumSet.

In other words, you rather go for:

public enum Suit { DIAMONDS, SPADE, ...

plus

public enum Value { TWO, THREE, ... JOKER ... whatever

to then have

public class Card {
  private final Suit suit;
  private finale Value value;

Meaning: you use enums to create a type that enumerates such kinds of values. And then your card objects simply get assigned Suit, Value, ... and so on. And by using enum and EnumSet you basically get the thing you are asking for ... for free.

Finally: good OOP is more than just putting extends here or there. Your classes model reality. A suit is not a card! The suit is a property of a card.

Upvotes: 1

C-Otto
C-Otto

Reputation: 5843

Write a dedicated class for the job, which internally uses a set. This class offers a public method to add a new card, and before really adding the card to the set it checks if it is allowed. From outside you don't have access to the private field containing the set, so you have to use the method that enforces the check.

Upvotes: 0

Related Questions