6

I'm trying to do something that would normally look like this in C:

typedef enum {
    HTTP  =80, 
    TELNET=23, 
    SMTP  =25, 
    SSH   =22, 
    GOPHER=70} TcpPort;

Approach 1

Here is what I have in Java, using enum:

public static enum TcpPort{
    HTTP(80),
    TELNET(23),
    SMTP(25),
    SSH(22),
    GOPHER(70);

    private static final HashMap<Integer,TcpPort> portsByNumber;
    static{
        portsByNumber = new HashMap<Integer,TcpPort>();
        for(TcpPort port : TcpPort.values()){
            portsByNumber.put(port.getValue(),port);
        }
    }
    private final int value;
    TcpPort(int value){
        this.value = value;
    }
    public int getValue(){
        return value;
    }
    public static TcpPort getForValue(int value){
        return portsByNumber.get(value);
    }
}

Approach 1 - Problems

I find that I am having to repeat this pattern in various places, but was wondering: is there a better way? particularly because:

  1. this seems convoluted and less elegant, and
  2. it also shifts something that would be compile time to run time".

One of the reasons I use this mapping, is because it looks better in switch statements, e.g.:

switch(tcpPort){
    case HTTP:
        doHttpStuff();
        break;
    case TELNET:
        doTelnetStuff();
        break;
    ....
}

I suppose there are also benefits of stronger type safety with enums.

Approach 2 I am aware I could do:

public static class TcpPort{
    public static final int HTTP   = 80;
    public static final int TELNET = 23;
    public static final int SMTP   = 25;
    public static final int SSH    = 22;
    public static final int GOPHER = 70;
}

but my feeling is that enums are still better. Is my enum approach above the way to go? or is there another way?

xirt
  • 514
  • 1
  • 6
  • 18
  • you can not compare incompartable things, simply because `java` has not any `define` construction – Andremoniy Apr 29 '15 at 13:53
  • If one were writing the code in C, one would just use a `#define`. Java has at least two ways to solve the same problem - use a `enum` or `static int`. Before I start stamping out `enum`s using the pattern above, I am checking with the community that there is not a better way, and that it is the correct approach. – xirt Apr 29 '15 at 13:58

3 Answers3

1

My feeling is that in purposes only for switch statement enum is superfluous in your case, and it is better simply using final static int constants. For instance of memory economy.

Also, Joshua Bloch in his Effective Java recommends using enums instead of int constants in his Item 30: Use enums instead of int constants. But IMHO it is correct way of enum using for more complicated cases then just replacing c #define construction.

UPDATE: as author mentioned in his comment to this my answer, he is wondering is use if enum is better then int constants in general. In this case such question becomes duplicate (see Java: Enum vs. Int), and my answer will: in general enums are better, and why - look at Joshua Bloch's Item 30, as I mentioned before.

Community
  • 1
  • 1
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • I have clarified the question - not just for case statements, but in general. – xirt Apr 29 '15 at 13:36
  • @xirt in this case you should also change topic of the post, not "enum vs define", but "enum vs int constants". Actually in such angle of view your question becomes duplicate or unclear. – Andremoniy Apr 29 '15 at 13:39
  • I have updated the title, and put some headings in the question to clarify. So - if _in general_ `enum`s are better, the implementation would be the pattern listed in **Approach 1** (i.e. it is ugly, but still the best?) – xirt Apr 29 '15 at 13:49
  • I think you have said that, but want to confirm: "go ahead with Approach 1" :-) – xirt Apr 29 '15 at 14:07
0

You are mixing two concepts here. #define allows you to use the preprocessor. The compiler will just know the value you defined.

In Java using Enums for just Port Numbers seems unreasonable, as they should be configurable imo.

I would recommend using an interface with factories (surounded by enumeration for default types, allowing 3rd party to add new interfaces [OOP].

Generally speaking i would not use Enumerations for constants, simply because they should be scoped within the proper class.

You can import constants with a static import if you want to increase readability.*

*I would consider static imports bad practice tho.

manf
  • 328
  • 4
  • 14
  • I'm sure, that OP knows about prerpocessor and how enums are working in java – Andremoniy Apr 29 '15 at 13:42
  • Probably, i just thought it might be important to note - considering the comparison between c and java. – manf Apr 29 '15 at 13:46
  • I am using port numbers as an example. The likelihood that port 80 will change to something other than HTTP is... low enough to be hard coded IMHO. – xirt Apr 29 '15 at 13:53
  • I have relplaced the #define preprocessor with enum, to make it a more apples for apples comparison. Same question applies. – xirt Apr 29 '15 at 15:22
0

You could create a couple of classes like this:

package com.ggl.testing;

import java.util.ArrayList;
import java.util.List;

public class TCPPort {

    private List<Port> ports;

    public TCPPort() {
        this.ports = new ArrayList<Port>();
        this.ports.add(new Port("HTTP", 80));
        this.ports.add(new Port("TELNET", 23));
        this.ports.add(new Port("SMTP", 25));
        this.ports.add(new Port("SSH", 22));
        this.ports.add(new Port("GOPHER", 70));
    }

    public Port getPortByName(String portName) {
        for (Port p : ports) {
            if (portName.equals(p.getPortName())) {
                return p;
            }
        }

        return null;
    }

    public Port getPortByNumber(int portNumber) {
        for (Port p : ports) {
            if (portNumber == p.getPortNumber()) {
                return p;
            }
        }

        return null;
    }

    public class Port {

        private final int portNumber;

        private final String portName;

        public Port(String portName, int portNumber) {
            this.portName = portName;
            this.portNumber = portNumber;
        }

        public int getPortNumber() {
            return portNumber;
        }

        public String getPortName() {
            return portName;
        }

    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111