jnovacho
jnovacho

Reputation: 2903

Singleton class with parameter

I'm developing computer vision application and I will need Classifier class. This class will be immutable per run of application and it loads the trained data from disk on initialization. I want to ensure that whole program will have access to same trained data and I want to block reloading the from disk once they are loaded.

What I was considering was to use either static class or singleton. I'm not sure how to load data to static class, because the path to data file is not know at compile time - it will be program argument. So I was thinking of Singleton pattern, but there I don't know how to initialize it dynamically.

My idea was to use following:

class Singleton {
    private static Singleton instance;
    private Singleton() { ... }
    private static SomeDataObject data;

    public static Singleton getInstance() {
        if(instance == null)
            instance = new Singleton();

            return instance;
    }

    public static init(string dataPath){
        if(data == null)
             loadDataFromFile(dataPath)
    }
}

This would not work, because I have no control which method will be called first.

I know the proper way would be to create the instance with data at the begining and pass it to all classes and methods which need it, but that's not really general solution. I can keep track of all calls to Classifier in my own code, but if I would make the code as API, this would be a problem.

In short how to initialize singleton at runtime?

Upvotes: 8

Views: 22027

Answers (2)

Bernhard Barker
Bernhard Barker

Reputation: 55609

I don't think (exactly) what you want to do would work.

The below would work:

public static void main(String[] args)
{
  Singleton.init("somepath");
  ...
  Singleton.getInstance().doingStuff();
  ...
}

A better implementation may be: (which would cause a NullPointerException if you try to use it without calling init first) (not really Singleton any more though)

private static Singleton instance;
private SomeDataObject data;

private Singleton(String path) { loadDataFromFile(path); ... }

public static Singleton getInstance() {
   return instance;
}

public static void init(String dataPath){
   instance = new Singleton(dataPath);
}

Then there's: (possible bad coding practice aside)

class Main
{
  public static void main(String[] args)
  {
    Singleton.currentPath = "somepath";
    ...
  }
}

class Singleton
{
  public static String currentPath = null;
  private static Singleton instance;
  private SomeDataObject data;

  private Singleton(String path) { loadDataFromFile(path); ... }

  public static Singleton getInstance() {
     if(instance == null && currentPath != null)
        instance = new Singleton(currentPath);
     return instance;
  }
}

which I suppose doesn't really solve much.

Upvotes: 8

BeCodeMonkey
BeCodeMonkey

Reputation: 91

I use something that is "more" threadsafe than the current winning solution with almost no synchronized used.

import java.util.function.Supplier;

public class InitOnce {

/**
 * Marked as final to prevent JIT reordering
 */
private final Supplier<String> theArgs;

private InitOnce(Supplier<String> supplier) {
    super();
    this.theArgs = supplier;
}

/**
 * Uses the arguments to do something
 * 
 * @return
 */
public String doSomething() {
    return "Something : " + theArgs.get();
}

/**
 * Initializes all the things
 * 
 * @param someArgs
 */
public static synchronized void init(final Supplier<String> someArgs) {
    class InitOnceFactory implements Supplier<InitOnce> {
        private final InitOnce initOnceInstance = new InitOnce(someArgs);

        @Override
        public InitOnce get() {
            return initOnceInstance;
        }
    }

    if (!InitOnceFactory.class.isInstance(instance)) {
        instance = new InitOnceFactory();
    } else {
        throw new IllegalStateException("Already Initialized");
    }
}

private static Supplier<InitOnce> instance = new InitOnceHolder();

/**
 * Temp Placeholder supplier
 * 
 */
private static final class InitOnceHolder implements Supplier<InitOnce> {
    @Override
    public synchronized InitOnce get() {

        if (InitOnceHolder.class.isInstance(instance))
            throw new IllegalStateException("Not Initialized");

        return instance.get();
    }

}

/**
 * Returns the instance
 * 
 * @return
 */
public static final InitOnce getInstance() {
    return instance.get();
}
}

Upvotes: 0

Related Questions