How to pass a parameter to a static initialization block

What I want to do is load key / value pairs from a file (excel file using Apache poi) into a static map, which will be used as a lookup table. After loading, the table will not change.

public final class LookupTable { private final static Map<String, String> map; static { map = new HashMap<String, String>(); // should do initialization here // InputStream is = new FileInputStream(new File("pathToFile")); // not sure how to pass pathToFile without hardcoding it? } private LookupTable() { } public static void loadTable(InputStream is) { // read table from file // load it into map map.put("regex", "value"); } public static String getValue(String key) { return map.get(key); } } 

Ideally, I want to load a map inside a static initialization block, but how would I transmit a stream without hard coding? The problem that I see using the static method loadTable may not be called before calling other static methods.

 // LookupTable.loadTable(stream); LookupTable.getValue("regex"); // null since map was never populated. 

Is there a better approach to this?

+4
source share
7 answers

You cannot pass information to static initialization blocks - they must work in isolation. Since the thread you plan to go through must be known before the program starts, presumably your LookupTable should also find it. For example, it may be some kind of configuration utility that provides a stream for you. Then you can write your initializer as follows:

 static { InputStream exelStream = MyConfigUtil.getExcelStreamForLookup(); loadTable(exelStream); } 

Presumably, the system has a class that could receive an Excel stream from a source that it knows. The source does not need hard coding: it can read the location from the configuration file or receive data from a predefined network folder on your server. In all cases, the process of getting the Excel stream should β€œdecline” somewhere in the sense that something on your system should find it without additional parameters.

+1
source

Yes, there is a better approach, use the Factory design template to initialize the object before you use it:

http://www.oodesign.com/factory-pattern.html

+2
source

Everything you use should be available at startup. As far as I know, your options are:

  • Hard-code path. This is bad for obvious reasons.
  • Static variable or static method. This is a problem with the chicken and the egg; it eventually becomes hardcoded, but at least you can search using the static method.
  • Use a Java variable or environment. So you should use something System.getProperty("filename", "/default/filename") . Better because it is at least configured using environment options or -D when starting the JVM.
  • Use the ClassLoader getResource* methods. This is probably the correct answer. In particular, you probably want to use the getResourceAsStream() method in the current context of the ClassLoader Thread.currentThread().getContextClassLoader() . (So, Thread.currentThread().getContextClassLoader().getResourceAsStream("filename") in general.) ClassLoader will then find your resource for you (as long as you put it in your right mind in CLASSPATH ).
+2
source

This is probably the case using lazy map loading.

But you will need to set inputFileName before calling getValue() for the first time. This will be done in your initialization code for applications. (Or you may have a static method to install it.)

This indicates the advantage of lazy loading. You don't have to have a file name until you call getValue() for the first time. Using a static initializer, you should get the file name stored somewhere outside the class so that it can be used to load data when the class loads (but after the initialization of the static fields.

  public static String inputFileName = null; public static String getValue(String key) { if (map == null) { map = = new HashMap<String, String>(); // open the file using 'inputFileName' loadTable(InputStream is); } return map.get(key); } 

If your code is multithreaded, let me know and I will comment on synchronization issues.

Alternative

You can also use Spring to introduce a map and build it in some other class - MapBuilder , for example.

0
source

Try using System.getProperty () and pass the parameter with -D on the command line.

 static String prop; static { prop = System.getProperty("java.home"); } public static void main(String... args) { System.out.println(prop); } 
0
source

This does not directly answer your question, but I do not understand why map should be static. You can change the map to non-static and change the constructor to public LookupTable(File file) {...fill map...} . Then you can have many instances of this class if you have different excel files; Now this may not be the case, but it will be a β€œfuture” code.

0
source

I would suggest a singleton enum approach if that suits your case.

 public enum LookupTable { INSTANCE(FileManager.getFileName()); LookupTable(String fileName){ props = new HashMap<String,String>(); //Read from excel and fill the hashmap } private final Map<String, String> props; public String getMapValue(String key){ return props.get(key); } } 

which can be called

 LookupTable.INSTANCE.getMapValue("mykey"); 

This will call these methods in order

  • Get the file name from the filemanager class, which is parameterized according to your needs
  • Call the constructor (it is private) and load the properties from the excel file
  • getMapValue for key and return

The next call to LookupTable.INSTANCE.getMapValue ("mysecondkey") will only call getMapValue, since INSTANCE is initialized in advance.

0
source

All Articles