Constants and simplifying enum value access - java

For an enum with only one member variable, is there a simpler (and best practices) way to access it without the use of an accessor method? I had alternatively considered using public static final variables in a constants class but the more I read, the more people suggest using enum as the way to encapsulate those values.
To attempt to illustrate what I mean, I've included the following example:
public enum FILE_NAME {
MAIN("MAIN.TXT"),
ATTACHMENT("ATTACHMENT.TXT"),
OTHER("OTHER.HTM");
private String fileName;
FILE(String fileName) {
this.fileName = fileName;
}
public String getfileName() {
return fileName;
}
}
I would then normally access that value like so:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(bos);
// Add file
zip.putNextEntry(new ZipEntry(FILE_NAME.MAIN.getFileName()));
For my particular use case, I'd much prefer to be access the filename with a call like:
...
zip.putNextEntry(new ZipEntry(FILE_NAME.MAIN));
In doing so, the code reduces the length (and almost syntactic redundancy of calling a filename of a file) of the call needed to access the MAIN file name text. While this may not even be feasible or desirable, I'm curious to know if it's worth considering.
Thanks.

To simplify it, and still keep it safe, use a public final String field for the file name:
public enum FileType {
MAIN("MAIN.TXT"),
ATTACHMENT("ATTACHMENT.TXT"),
OTHER("OTHER.HTM");
// Name in all-caps to make it look like the constant it is
public final String FILENAME;
private FileType(String fileName) {
this.FILENAME = fileName;
}
}
To use it:
zip.putNextEntry(new ZipEntry(FileType.MAIN.FILENAME));
Note that change of class name to "FileType" to better adhere to java standards.

It seems like the following would work better
public enum FILE_NAME {
MAIN("MAIN.TXT"),
ATTACHMENT("ATTACHMENT.TXT"),
OTHER("MAIN.TXT");
private String fileName;
FILE(String fileName) {
this.fileName = fileName;
}
public void putEntry ( ZipOUtputStream zip )
{
zip.putNextEntry(new ZipEntry(this.getFileName()));
}
public String getfileName() {
return fileName;
}
}
Then you could use it like:
FILE_NAME.MAIN.putEntry(zip);

You can have aliases
enum Pet
{
cat("meow"),
dog("woof")
String getSound()
}
public static final Pet CAT = Pet.cat;
public static final String MEOW = Pet.cat.getSound();

Related

Understanding singletons class in Java

When reading Effective Java I found that the most useful approach to implement singletons is just using enums with single INSTANCE. But in case we need to make it a class, according to some of his susggestions the perfect serializable thread-safe singleton class would look like this:
public class SerializableSingletonClass implements Serializable{
private static final long serialVersionUID = 1L;
private int value;
private String name;
private SerializableSingletonClass(int value, String name) {
if( value < 0 ) throw new IllegalArgumentException("Value may not be less than 0");
this.value = value;
this.name = Validate.notNull(name, "Name may not be null");
}
private static class SerializableSingletonHolder{
public static final SerializableSingletonClass INSTANCE;
static {
INSTANCE = new SerializableSingletonClass(0, "default");
}
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException{
throw new InvalidObjectException("proxy required");
}
private Object writeReplace(){
return new SerializationProxy(this);
}
private static class SerializationProxy implements Serializable{
private static final long serialVersionUID = 1L;
public SerializationProxy(SerializableSingletonClass ignored) { } //Here is the question
private Object readResolve(){
return SerializableSingletonHolder.INSTANCE;
}
}
}
As far as I got he proposed to just replace serialized instances with one that's used currently.
So, why do we ever need to make singletons serializable in the owing that their serial form would not be ever used. We just throw it out and replace deserialized instance with the current one.
To me the problem of implementing serializable singletons looks like only a theoretical one. Accoring to what I said, deserializing singleton doesn't make much sense (in the case of enums either).
So what did I miss? Couldn't you explain it in a bit more detail?
enums are also serializable, just not their fields.
I think it's important to keep it simple and easy to maintain e.g. use a text file if you are going to save it.
I would add a method like save and load to it (possibly with a filename)
enum SaveableSingleton {
INSTANCE;
String name;
int value;
public void load(String filename) throws IOException {
try (Scanner scanner = new Scanner(new File(filename))) {
name = scanner.nexLine();
value = scanner.nextInt();
}
}
public void save(String filename) throws IOException {
try (PrintWriter pw = new PrintWriter(new File(filename))) {
pw.println(name);
pw.println(value);
}
}
}
creates a file like
my-name-is
12345
If you have some field in some other serializable class where you store this singleton instance and want to serialize / deserialize that class, you would always have to mark that field transient and make sure to fill it manually on deserialization. If you handle it in the Singleton, you don't have to do this. I guess that is the reason for a serializable singleton.

How to simplify multiple same looking enumerations?

I got multiple enumerations, which all implement the same interface AnimationSet, but with some more fields, such as filename and each enum had to implement the exact same getFilename().
interface AnimationSet{
String getPath();
String getFilename();
}
public enum HammerAnimationSet implements AnimationSet{
ONE("1"),
TWO("2"),
THREE("3"),
FOUR("4");
private static String path = "hammer/";
private String filename;
private HammerAnimationSet(String filename){
this.filename = filename;
}
public String getFilename(){
return filename;
}
}
public enum ShopAnimationSet implements AnimationSet{
HEART("heart"),
MANA("mana bottle"),
LUCK("clover");
private static String path = "shop/";
private String filename;
private ShopAnimationSet(String filename){
this.filename = filename;
}
public String getFilename(){
return filename;
}
}
Edit: I now use these enums to fill a List<List<List<Image>>> where the first list corresponds the enum and a group of animations, the second list consists of all the animations in the group, and the third list (with images) represent all the images in one specific animation.
Later I can get an image from the outside from this list.
Now I was looking for a way to simplify/shorten this code (The methods and fields are all the same).
I thought about a superclass, but that was not possible because an enum already extends Enum. I also thought about changing the enumerations to classes, but I would like to keep the enum values. I also thought about an abstract interface, but what I thought about wasn't possible.
Could you help thinking for a way to achieve simpification.
You might consider using delegation instead of inheritance:
public class AnimationSet {
private final String path, filename;
public AnimationSet(String path, String filename) {
this.path = path;
this.filename = filename;
}
public String getPath() {
return path;
}
public String getFilename(){
return filename;
}
}
public enum HammerAnimationSet {
ONE("1"),
TWO("2"),
THREE("3"),
FOUR("4");
final AnimationSet animationSet;
HammerAnimationSet(String filename) {
animationSet=new AnimationSet("hammer/", filename);
}
public AnimationSet getAnimationSet() {
return animationSet;
}
}
public enum ShopAnimationSet {
HEART("heart"),
MANA("mana bottle"),
LUCK("clover");
final AnimationSet animationSet;
private ShopAnimationSet(String filename){
animationSet=new AnimationSet("shop/", filename);
}
public AnimationSet getAnimationSet() {
return animationSet;
}
}
So the features of AnimationSet can be expanded without the need to adapt any of the enums.

OOP - Creational Pattern (Factory Method) Am I on the right path?

I'm learning design patterns and at this stage, I'm playing around with creational patterns, Factory Method to be precise.
The idea is to have a Factory Method to create either documents or links(symlinks) in a "virtual" filesystem.
Would someone be willing to take a look at the code and advice me and guide me to the right path ?
FileFactory.java
public final class FileFactory {
public FileFactory(){}
public static IFileFactory createSoftLink(){
return new SymbolicLinkFactory();
}
public static IFileFactory createDocument(){
return new DocumentFileFactory();
}
Interface IFileFactory.java
public interface IFileFactory {
FileSystemElement createFile (String name, String mimeType, String currentDirectory, String user) throws IOException;}
DocumentFileFactory.java
public class DocumentFileFactory implements IFileFactory {
protected DocumentFileFactory() {}
#Override
public mFile createFile (String name, String mimeType, String currentDirectory, String user) throws IOException {
String fName = name;
if (mimeType.equalsIgnoreCase("docx")) {
fName += ".docx";
}else if (mimeType.equalsIgnoreCase("pptx")) {
fName += ".pptx";
}else if (mimeType.equalsIgnoreCase("xlsx")) {
fName += ".xlsx";
}else if (mimeType.equalsIgnoreCase("docm")) {
fName += ".docm";
}else if (mimeType.equalsIgnoreCase("pptm")) {
fName += ".pptm";
}else if (mimeType.equalsIgnoreCase("xlxm")) {
fName += ".xlxm";
}else {
fName += "."+mimeType;
}
mFile file = new mFile();
file.rename(fName);
file.create(fName, currentDirectory, user);
Path filePath = Paths.get(currentDirectory+System.getProperty("file.separator")+fName);
file.setPath(filePath);
return file;
}
SymbolicLinkFactory.java
public class SymbolicLinkFactory implements IFileFactory {
private FileSystemElement fsElement;
protected SymbolicLinkFactory() {}
#Override
public FileSystemElement createFile(String name, String mimeType, String currentDirectory, String user) throws IOException {
SoftLink sl = new SoftLink(name, fsElement, fsElement.getPath(), Paths.get(currentDirectory));
return sl;
}
public void setFileSystemElement(FileSystemElement fsElement) {
this.fsElement = fsElement;
}
In general it looks fine to me. It would be better if you had some logic that showd why you would use the factory pattern, for example something that checks if the document you want to create already exists, and returns a softlink creator if it does.
As mentioned by Josh, the implementation of createFile is a bit confusing, and diverts attention from your problem/goal.
As the code stands, there is no need for the FileFactory class. All it does is to centralize/shortcut to your implementations of the IFileFactory interface.
There are two other common use cases for factory class that might be relevant to your situation:
Dependency injection: Hide which implementation is actually used
inside the FileFactory class, allow to switch implementation at
startup/runtime
Factory/class families: Here you are only creating
one instance. Imagine instead that you had two kinds of items - file
item and folder. In that case it's important that a "file item" you
create is of the same kind as your "folder", for exampl eso a file
system file goes in a file system folder, and a Wiki page goes in a
Wiki URL.

how do I access this class

import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
public class LoadSettings
{
public static void LoadMySettings (Context ctx)
{
SharedPreferences sharedPreferences = ctx.getSharedPreferences("MY_SHARED_PREF", 0);
String strSavedMem1 = sharedPreferences.getString("gSendTo", "");
String strSavedMem2 = sharedPreferences.getString("gInsertInto", "");
String cCalId = sharedPreferences.getString("gCalID", "");
setInsertIntoStr(strSavedMem2);
setSendToStr(strSavedMem1);
}
private static String cSendToStr;
private static String cInsertIntoStr;
private int cCalId;
private Uri cCalendars;
public String getSendToStr()
{
return this.cSendToStr;
}
public static void setSendToStr(String pSendToStr)
{
cSendToStr = pSendToStr;
}
public String getInsertIntoStr()
{
return this.cInsertIntoStr;
}
public static void setInsertIntoStr(String pInsertIntoStr)
{
cInsertIntoStr = pInsertIntoStr;
}
}
from the calling class i have tryed lots the current is.
LoadSettings.LoadMySettings(this);
but when i try to get some data for example.
textSavedMem1.setText(LoadSettings.getSendToStr());
i get a Null error.
LoadMySettings is not a class but a method (so it should start with a lower case, if you follow Oracle/Sun's naming conventions for the Java language).
You access it indeed by calling LoadSettings.loadMySettings(someContext), where someContext is the context to pass around. In your example, we don't know what this refers to, so maybe your error lies there.
Then when you do this: textSavedMem1.setText(LoadSettings.getSendToStr());
You call a non-static method, so that should be either using an instance of LoadSettings or, more likely considering your code, you could change getSendToStr to be:
public static String getSendToStr()
{
return cSendToStr;
}
Though that seems to be rather bad design.
Maybe if you tell us more about what you try to do, we can help more, as as such our answers will just take you one step further.
EDIT: Ok, I just figured out what you are trying to do...
You need to go back and learn basic Java concepts and read on access modifiers, and constructors first, and OO semantics in Java in general.
Change your class to this:
public class LoadSettings
{
public LoadSettings(Context ctx)
{
SharedPreferences sharedPreferences =
ctx.getSharedPreferences("MY_SHARED_PREF", 0);
String strSavedMem1 = sharedPreferences.getString("gSendTo", "");
String strSavedMem2 = sharedPreferences.getString("gInsertInto", "");
String cCalId = sharedPreferences.getString("gCalID", "");
setInsertIntoStr(strSavedMem2);
setSendToStr(strSavedMem1);
}
private String cSendToStr;
private String cInsertIntoStr;
private int cCalId;
private Uri cCalendars;
public String getSendToStr()
{
return cSendToStr;
}
public void setSendToStr(String pSendToStr)
{
cSendToStr = pSendToStr;
}
public String getInsertIntoStr()
{
return cInsertIntoStr;
}
public void setInsertIntoStr(String pInsertIntoStr)
{
cInsertIntoStr = pInsertIntoStr;
}
}
And create a new instance of LoadSettings with:
LoadSettings mySettings = new LoadSettings(someContext);
You can then correctly invoke:
textSavedMem1.setText(mySettings.getSendToStr());
Haylem has the right of it, but I just wanted to add a comment:
There are basically two design patterns in Java for what you're trying to do. One is the static class where all the methods and variables are static and you access them as e.g.
LoadSettings.loadMySettings(this);
string = LoadSettings.getSendToStr()
// etc.
The other pattern is the "singleton" class where you create exactly one instance of the class and you access the instance:
LoadSettings ls = new LoadSettings(this);
ls.loadMySettings();
string = ls.getSendToStr();
Either way is good, but what you're doing is a mish-mash of both methods and it won't work.

Java XML Serializing, Missing fields in file

this is the issue at hand, when trying to serialize the class below with the code below i'm getting is the below xml file without all the strings in the class.
The Class (some static values have changed but basically it), I left out all the generated get\set but they are all there with public access modifiers.
public class NotificationConfiguration implements Serializable
{
public static final String PORT_KEY = "mail.smtp.port";
public static final String DEFAULT_PORT_VALUE = "587";
public static final String TTL_KEY = "mail.smtp.starttls.enable";
public static final String DEFAULT_TTL_VALUE = "true";
public static final String AUTH_KEY = "mail.smtp.auth";
public static final String DEFAULT_AUTH_VALUE = "true";
public static final String MAIL_SERVER_KEY = "mail.smtp.host";
public static final String DEFAULT_MAIL_CLIENT_HOST = "smtp.gmail.com";
public static final String DEFAULT_MAIL_CLIENT_USERNAME = "*********";
public static final String DEFAULT_MAIL_CLIENT_PASSWORD = "*********";
public static final String DEFAULT_MAIL_CLIENT_ADDRESS = "*********";
public static final String DEFAULT_ADMIN_EMAIL = "*********";
public static final long DEFAULT_MAIL_INTERVAL = 24*60*60*1000; //One time a day default
public static final String SAVED_FOLDER_NAME = "C:\\Library";
public static final String SAVED_FILE_NAME = "C:\\Library\\NotificationCfg.xml";
private String portValue = DEFAULT_PORT_VALUE;
private String ttlValue = DEFAULT_TTL_VALUE;
private String authValue = DEFAULT_AUTH_VALUE;
private String mailClientHost = DEFAULT_MAIL_CLIENT_HOST;
private String mailClientUserName = DEFAULT_MAIL_CLIENT_USERNAME;
private String mailClientPassword = DEFAULT_MAIL_CLIENT_PASSWORD;
private String mailClientAddress = DEFAULT_MAIL_CLIENT_ADDRESS;
private String adminEMail = DEFAULT_ADMIN_EMAIL;
private boolean overdueSubsNotificationEnabled = false;
private boolean adminReportNotificationEnabled = false;
private long mailInterval =
}
The code used to serialize, which also creates the folder if missing.
public void storeChanges()
{
try
{
try
{
File f = new File(NotificationConfiguration.SAVED_FOLDER_NAME);
f.mkdir();
}
catch (Exception e){}
XMLEncoder encoder = new XMLEncoder( new BufferedOutputStream(new FileOutputStream(NotificationConfiguration.SAVED_FILE_NAME)));
encoder.writeObject(notificationConfig);
encoder.close();
System.out.println(LOG_CONFIGURATION_STORED);
}
catch (Exception ex)
{
System.out.println(LOG_CONFIGURATION_NOT_STORED + ex.getMessage());
}
}
The XML file received, with no exceptions thrown while serializing.
It basically just has the long value.
XMLEncoder encodes information about how to restore your object. If field values haven't changed from their defaults, XMLEncoder doesn't store anything.
This can cause confusion.
Hence, my rules of thumb when using XMLEncoder are:
1. don't initialize fields. don't do private String foo = DEFAULT_FOO;
2. don't do anything in the default constructor.
3. have some other method, or factory that will give you a "default" setup if needed.
I highly recommend to read again the XMLEncoder Javadoc
I will point out the main differences with the binary serialization we all know.
to restore the instance it need the class definition available to the JVM
It serializes only the data. And only the modified from default data.
As result of the 2 points above - is that there is no reason to serialize Static final values - they are part of the class definition.
The binary serialization on the other hand does serialize the class definition and can load from byte stream a class that was not available to the JVM before.
That is why you got results that you see. It Ok this is behavior by design and you use it right. It seems just not to be what you need.
By the way see what Xstream has to offer.
What is SAVED_FOLDER_NAME ? Is that like a factory object and did you by any chance call setMailInterval on that object?
Could that be that only mailInterval has a getter?
Just looked again the question apparently there is getter for all fields so ...

Categories