Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning a final field in a try-catch block within a constructor

So, I'm trying to initialize a DatagramSocket in a constructor, and I want this field to be final, but my compiler (i.e. Eclipse) is giving me the following error:

The blank final field datagramSocket may not have been initialized

This is understandable. Here's a code snippet:

    public class Foo
    {
        private final int DEFAULT_UDPLISTENPORT = 49400;
        private final DatagramSocket datagramSocket;

        public Foo()
        {
            synchronized(this)
            {
                try
                {
                    datagramSocket = new DatagramSocket(DEFAULT_UDPLISTENPORT);
                }
                catch (SocketException e)
                {
                    // Log error
                    logger.error("Trouble opening UDP port: ", e);
                }
            }
        }
    }

Now, I know there's a way to bypass this, but it requires me to create a temporary variable. Here's a code snippet:

    public class Foo
    {
        private final int DEFAULT_UDPLISTENPORT = 49400;
        private final DatagramSocket datagramSocket;

        public Foo()
        {
            synchronized(this)
            {
                DatagramSocket tempSocket = null;
                try
                {
                    tempSocket = new DatagramSocket(DEFAULT_UDPLISTENPORT);
                }
                catch (SocketException e)
                {
                    // Log error
                    logger.error("Trouble opening UDP port: ", e);
                }

                datagramSocket = tempSocket;
            }
        }
    }

So, I suppose my question is: is there a more elegant way of doing this, or is this something that I'll just have to live with if I want that field to be final?

EDIT:

For those of you who are interested, here's the solution I came up with from your recommendations:

public class Foo
{
    private static final Foo INSTANCE;
    static
    {
        try
        {
            INSTANCE = new Foo();
        }
        catch (SocketException e)
        {
            throw new ExceptionInInitializerError(e);
        }
    }
    private final int DEFAULT_UDPLISTENPORT = 49400;
    private final DatagramSocket datagramSocket;

    public Foo() throws SocketException
    {
        synchronized (this)
        {
            datagramSocket = new DatagramSocket(DEFAULT_UDPLISTENPORT);
        }
    }

    public static Foo getInstance()
    {
        return INSTANCE;
    }
}

Please, let me know if this is correct, or if you have any other suggestions. I appreciate the help!

like image 524
mre Avatar asked May 02 '11 14:05

mre


People also ask

Can you enclose constructor in try catch block?

Yes, we can write try catch and finally block in a constructor ant it works properly.

What is the final part of try catch block?

catch statement is comprised of a try block and either a catch block, a finally block, or both. The code in the try block is executed first, and if it throws an exception, the code in the catch block will be executed. The code in the finally block will always be executed before control flow exits the entire construct.

Can you have a try catch in a constructor?

yes we can write a try catch block within a constructor same as we can write in inside a method.

How can a final field be set to a value?

When a field is defined as final , it has to be initialised when the object is constructed, i.e. you're allowed to assign value to it inside a constructor. A static field belongs to the class itself, i.e. one per class. A static final field is therefore not assignable in the constructor which is one per object.


2 Answers

Yes, after catching SocketException wrap it in runtime exception and rethrow it. Since your variable is final and you have encountered error during object initialization, your object is probably in incorrect state and you are guaranteed that it will remain as such.

Logging the exception isn't probably enough for exception handling and hiding SocketException hides the fact that the object is invalid and allows you to continue, risking NullPointerException or others.

If you really want to create such a faulty object, your suggestion is fine, just use another method:

public Foo()
    {
        synchronized(this)
        {
            datagramSocket = createSocket();
        }
    }

private DatagramSocket createSocket() {
        try
        {
            return new DatagramSocket(DEFAULT_UDPLISTENPORT);
        }
        catch (SocketException e)
        {
            logger.error("Trouble opening UDP port: ", e);
            return null;  //I beg you, don't return null here...
        }
 }

As for returning null: consider subclassing DatagramSocket and creating:

  • NoOpDatagramSocket

  • NullDatagramSocket

  • BrokenDatagramSocket

  • MemoryDatagramSocket

  • ...you get the idea :-)

P.S.: Why the synchronized?

P.S.2: The comment // Log error right before logger.error() isn't adding much value, don't you think?

like image 185
Tomasz Nurkiewicz Avatar answered Nov 11 '22 01:11

Tomasz Nurkiewicz


A possible alternative is make your constructor throw SocketException. This will get rid of the need for the try-catch block that forces you to use the temporary variable.

like image 43
Kal Avatar answered Nov 11 '22 02:11

Kal