2

EDIT

Please note my question is more to understand if there is any best practice around it. The problem I am describing can be tackled many way, in fact I am handling it by throwing Runtime Exception. But is it the best practice? or should we fall back to more conventional ways of Singleton creation using a static method and decoupling exception from object creation

I am using enum to instantiate a singleton as prescribed in Effective Java

public enum MailApp { 
INSTANCE;   
private Gmail mailService;

private MailApp() {
    try {
        mailService = GoogleMailAppFactory.getGmailService();
    } catch (IOException e) {
        throw new AssertionError(e);
    }
}

The problem is if I add throws like following

public enum MailApp {

INSTANCE;

 /** Application name. */

private Gmail mailService;

private MailApp() throws IOException {
    mailService = GoogleMailAppFactory.getGmailService();
}

This will not compile saying Unhandled Exception

I have taken a look at How to throw an exception from an enum constructor?

I could not find any conclusive answer. So my question: What are the best practices for this? Is throwing an AssertionError only way forward?

Thanks for your help

Edit

My "MailApp" is only valid if we have a valid email service. In short I dont want to create an empty object. Could you please throw some light why this might be a bad idea and how this should be approached?

Community
  • 1
  • 1
Abhijit Mazumder
  • 8,641
  • 7
  • 36
  • 44
  • whatis tour goal? how you want your application behave it mailservice create exception on creation? that should be your first questio really. i suggest to handle exception inside constructor, and have additional method on singleton isAvailable – user902383 Jul 02 '15 at 09:53
  • 1
    Are you sure it is a good idea to init a mail service within an enum?! – Uwe Allner Jul 02 '15 at 10:07
  • Uwe, for me MailService is just any Backend API. My "GmailApp" is only valid if we have a valid email service. In short I dont want to create an empty object. Could you please throw some light why this might be a bad idea and how this should be approached? Also my enum is only for singleton, not per se a constant, as prescribed in Josh Bloch's Effective Java which is a bible for me and there lies the confusion :) – Abhijit Mazumder Jul 02 '15 at 11:19
  • @UweAllner I agree, probably better off using a `Map`, where the `MailApp` is a wrapper around a specific singleton instance, possibly holding a `Weak Reference` to the connection. It would allow `mailerMap.get(GMAIL).send(message)`; – Parker Jul 02 '15 at 11:36
  • @vallismortis but how do you create the singleton? – Abhijit Mazumder Jul 02 '15 at 11:39
  • 1
    @AbhijitMazumder Enumerated types are instantiated when they are loaded by the classloader, so if there were a problem connecting to any individual service, it would throw an exception immediately upon running the application. A better practice would be to de-couple the exception-raising method invocation from the enumerated type, so that any exceptions will be raised at appropriate times (such as sending/receiving a message). – Parker Jul 02 '15 at 11:41
  • @ vallismortis I totally get your point,.But I dont want to create an empty object and inject mailService. If I understand you correctly thats what you mean by decoupling. Moreover if you see Effective Java it says the best way to create a singleton is by using enum and I was trying to follow that. Now my object does not make sense without mailservice which I want to initialize in constructor. What I am trying to figure out is what should be the best practice. Should we fallback on more conventional way of creating singleton? – Abhijit Mazumder Jul 02 '15 at 17:56

3 Answers3

0

You may want to consider rethrowhing ioexception as a RuntimeException.

David Soroko
  • 8,521
  • 2
  • 39
  • 51
  • Hi David, if you see I am throwing an assertionerror as a RuntimeException which is more kind of appropriate – Abhijit Mazumder Jul 02 '15 at 11:20
  • I was not precise in my comment, what I meant is that you may want to re-throw the IOException as an unchecked exception (either a `RuntimeException` or a subclass of such) e.g.: catch (IOException e) { throw new RuntimeException(e); } – David Soroko Jul 02 '15 at 13:23
  • Ok, I understand you comment now, since `AssertionError` is unchecked why do you declare that `MailApp` throws IOException? – David Soroko Jul 02 '15 at 13:36
0

COMPLEX EXAMPLE:

import android.content.Context;

import java.util.HashMap;
import java.util.Map;


public enum Gender {

     /** non mather */
     NONE(0,R.string.any),

    /** Male */
    MALE(1,R.string.male);

    private final int _rId;
    private final int _id;

    private Context _context;

    public void  setContext(Context context) {
        _context = context;
    }

    private Gender(int id, int rId) {
        _id = id;
        _rId = rId;

        /** get context from setter */
        if(_context==null) {
            throw new NullPointerException("Exception");
        }

    }

    /**
     * @return The string representation of this element in the enumeration.
     */
    public int getResId() {
        return _rId;
    }

    public int getId() {
        return _id;
    }

    public static Gender getById(int id ) {
        return byId.get(id);
    }

    public String getName(){
        return _context.getResources().getString(_rId);
    }

    @Override
    public String toString(){
        return getName();
    }

    private static final Map<Integer, Gender> byId = new HashMap<>();
    static {
        for (Gender gender : Gender.values()) {
            if (byId.put(gender.getId(), gender) != null) {
                throw new IllegalArgumentException("duplicate id: " +gender.getId()); 
            }
        }
    }
}

EDIT:

  • try implement Singletons by simply make an enum type with one element
  • enum singleton guarantee that it's instance will be once per VM.

basicaly

public final class ISingleton {
    public final static ISingleton INSTANCE = new ISingleton(); 
}
ceph3us
  • 7,326
  • 3
  • 36
  • 43
  • Hi Tomsaz, If you see my code I used the same concept using AssertionError – Abhijit Mazumder Jul 02 '15 at 18:17
  • java.lang.Error And Errors are not meant to be caught They indicatiing about programming error.Imagine when we got OutOfMemoryError and you catch it while your app is still running. – ceph3us Jul 02 '15 at 18:42
  • Yep I get it. Good point, Thanks. Any other you can think of? Do you even think enum singleton is a good way to create Singleton? – Abhijit Mazumder Jul 02 '15 at 18:45
0

I would actually disagree with this approach specifically because of the issue you are dealing with.

I would implement singletons if I have to in the SE environment as follows:

public final class SingletonClass {
    private static SingletonClass INSTANCE;

    public SingletonClass getInstance() {
        return INSTANCE;
    }

    private SingletonClass() throws WhateverException {
       ...
    }
    static {
       try {
           INSTANCE = new SingletonClass();
       } catch (WhateverException e) {
           throw new RuntimeException(e);
       }
    }
}

The reason why I emphasize if I have to is because I try to avoid it in the first place because it is rare I would have only one of something. And with the notion of Dependency Injection using CDI or Picocontainer which works on SE environments as well though CDI has a lot of baggage and a lot more confusing to diagnose compared to Picocontainer.

On an JEE6 environment I would use the @javax.ejb.Singleton annotation. That guarantees that there is only one instance in a clustered deployment without a lot of fuss.

To clarify why the above works:

Community
  • 1
  • 1
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
  • Correct. the final one **What you are building is a class, not an enum** always used to bother me. I have been even reading using enum as singleton is at least a debatable idea. So the usecase I mentioned seems to me a perfect opportunity for me to ask for opinions in stackoverflow. – Abhijit Mazumder Jul 02 '15 at 18:55
  • In addition, this handles Errors such as out of memory (I'll update the answer to note that) – Archimedes Trajano Jul 02 '15 at 19:14