0

I want to create a class that would provide me with a card i.e

Card c = new Card(1) // where the builder should get values 1 to 4, representing 1=spade, 2=heart, 3=diamond, 4=clubs 
c.getType() // spade

I want the class to check during compilation time whether the card is of type spade, heart, diamond or club, and if not - it'll raise an error and won't let me compile. The class should NOT use enum

Edit: This question is asked solely for the purpose of understanding how would one answer that kind of question in the pre-enum era. I was asked this question during a job interview a couple of years ago

  • I updated the question. It should get a number, where each number represents a different kind of card. – Max Shapiro May 06 '20 at 16:46
  • 2
    Why is there a requirement to avoid enums? They are there exactly for these kinds of scenarios. Other than building your own enum-like types (as we used to before Java had enums), there isn't much you can do for compile-time safety. And in your hypothetical example: what would you expect `int x = someMethod(); new Card(x)` do? Compile? – Joachim Sauer May 06 '20 at 16:49
  • @JoachimSauer well I don't want to use enums exactly for the purpose of understanding how things used to be. expanding my horizons and getting a deeper understanding. As for your question, this piece of code should compile if and only if x is in the range of 1 to 4. – Max Shapiro May 06 '20 at 16:56
  • I'm assuming that this is a class project of some kind? Please indicate in your question if this is true. – NomadMaker May 06 '20 at 16:56
  • @NomadMaker actually no. that's a question I was once asked during a job interview. – Max Shapiro May 06 '20 at 16:59
  • Did you ask the interviewer what his answer was? Because, given your parameters, I'm not sure there is a way in Java to do this. – NomadMaker May 06 '20 at 17:00
  • I did, it was a couple of years ago and I can't recall his answer. The question just popped to my head again and I couldn't figure out the answer. – Max Shapiro May 06 '20 at 17:02
  • Are you sure it was at compilation time and not on runtime? – jhamon May 06 '20 at 17:05
  • 100%. I think that the answer @Mike gave was exactly what they asked for – Max Shapiro May 06 '20 at 17:10
  • @MaxShapiro: in the future, mentioning this kind of motivation in the question can be helpful, because I don't genuinely want to suggest anyone implement that now (since `enum` is simply a better solution), but the Pre-Java-5 way to do this is the [Enumerated Type Pattern](http://wiki.c2.com/?EnumeratedTypesInJava) (mentioned in [this question](https://stackoverflow.com/questions/1038321/alternative-to-enum-in-java-1-4)). – Joachim Sauer May 06 '20 at 17:10
  • @JoachimSauer Thank you, duly noted. And thank you for the links, I'll check them out. – Max Shapiro May 06 '20 at 17:13

3 Answers3

4

Update: there is no good way to enforce the value being between 1 and 4 (perhaps using some custom complex compile time annotation would work but I doubt it) as has been pointed out. This is just one way to enforce not allowing Card to be instantiated without using enums, and it implicitly keeps the value between 1 and 4 since each subclass sets its own value.

One way would be to make Card abstract (could also use an interface):

public abstract class Card {

    // should be 1 to 4
    protected int value;

    public int getValue() {
        return value;
    }
}

Then create child classes for each type:

public class Spade extends Card {
    public Spade() {
        this.value = 1;
    }
}

public class Heart extends Card {
    public Spade() {
        this.value = 2;
    }
}

This would throw a compile time error if someone tried to create a Card directly:

Card c = new Card();

Before enums existed, this is how most Java code handled enumerations: using a preset list of integers that were very error prone and not easily enforceable. It was a dark time, let's not go back :)

Mike
  • 4,722
  • 1
  • 27
  • 40
1

Java has no features to make new Card(4) compile fine, and new Card(5) to cause a compile-time failure. Can't be done.

The obvious thing to use here is enums. If you have some crazy requirement that prevents you from using it, well, do the usual thing when faced with crazy requirements: Accept that code style and tooling is going to suffer.

You could make subtypes for each suit (as shown by the answer from @Mike), but that isn't really suitable. subclasses are meant to represent different behaviour, and there is nothing different about a Hearts vs. a Spade.

Just so we're clear, let me repeat that: What you ask for is impossible.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
0

Thank you all for your answers, and thank you @JoachimSauer, I checked the link you gave me about pre 1.5 and the Type Safe Enum Pattern and I managed to get an answer I like:

class Solution {

    public static class Card {

        public static Card SPADE = new Card();
        public static Card HEART = new Card();
        public static Card DIAMOND = new Card();
        public static Card CLUB = new Card();

        public Card() {
        }

    }
    public static void main(String[] args) {
        Card c = new Card().SPADE; // Only Spade, Heart, Diamond, Club can be "manufactured"
    }
}

I know that it's not exactly what I've described, but only upon reading I understood what exactly I had in mind

of course thanks to @Mike for giving me the answer for what I managed to describe

  • This is not at all a good idea. 'new Card().SPADE' is making a new card, and then tossing that object out. The point of this pattern is to have a _PRIVATE_ constructor. Also, 'SPADE' is not a Card; it's a suit. Card.SPADE; is what you're looking for (as well as 'private Card() {}'. – rzwitserloot May 06 '20 at 20:31