How to simplify multiple same looking enumerations? - java

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.

Related

String enum in Java

I have an enum:
public enum ListEnums {
TEST("test1"),
TEST2("test2");
private final String txt;
ListEnums(String str){
txt = str;
}
#Override
public String toString(){return txt;}
I want get the enum string without call .toString().
Like:mymethod(ListEnums.TEST);
No: mymethod(ListEnums.TEST.toString());
Is it possible?
EDIT
The string return must be contains special chars.
here:
public enum ListEnums {
TEST("test1"),
TEST2("test2);
private final String txt;
ListEnums(String str){
txt = str;
}
#Override
public String toString(){
return txt;
}
if you call ListEnum.TEST.name() you will get TEST which is almost the same as calling toString()... if you instead do ListEnum.TEST then the name will be printed...
so Renaming the Enum constants is the way to go...
and the best part is: you will get rid off the constructor, the toString method and the variable txt...
you just dont need it anymore. :)
It's OK, and a good practice to use getters in your enums. Also good to keep your constructor private (though enums are private by default)....
public enum ListEnums {
TEST("test1"),
TEST2("test2");
private final String txt;
private ListEnums(String str){
txt = str;
}
public String getTxt() {
return txt;
}
}
public static void main(String[] args) {
System.out.println(ListEnums.TEST.getTxt());
}

Looping over rest of tab while creating object Java

Sorry for my title which is unclear, but I don't know how to say it differently.
I have an object "Rapport" which takes a couple of parameters (3 Strings and one Object). This Object "SortingParameter" takes a number of boolean arguments "boolean... args". My goal is to read lines in a text file, create an ArrayList made of these "Rapport" objects. For this, I loop through the lines:
for (String line : Files.readAllLines(Paths.get("myTxt.txt"),Charset.forName("ISO-8859-1"))) {
String[] split = line.split(";");
if(split.length>3){
rapports.add(new Rapport(split[0],split[1],split[2],new SortingParameter(PROBLEM)));
}else{
rapports.add(new Rapport(split[0],split[1],split[2]));
}
I would like to add the rest of my split[ ] tab in the object dynamically. Does anybody know how this could be cleanly done?
The rest of my code:
Rapport.java
package model;
import static model.Constant.RESSOURCES;
public class Rapport {
private String nomListe;
private String nomRessource;
private String categorie;
private SortingParameter param;
private boolean hasParameter;
public Rapport(String nomListe, String nomRessource, String categorie, SortingParameter param){
this.nomListe = nomListe;this.nomRessource = RESSOURCES + nomRessource;
this.categorie = categorie;this.param = param;
this.hasParameter = true;
}
public Rapport(String nomListe, String nomRessource, String categorie){
this.nomListe = nomListe; this.nomRessource = RESSOURCES + nomRessource;
this.categorie = categorie; this.param = null; this.hasParameter = false;
}
/** Getters **/
public String getNomListe(){return nomListe;}
public String getNomRessource(){return nomRessource;}
public String getCategorie(){return categorie;}
public boolean hasParameter(){return hasParameter;}
public SortingParameter getParam(){return param;}
}
And my SortingParameter.java:
package model;
import java.util.ArrayList;
public class SortingParameter {
private ArrayList<Boolean> paramList;
public SortingParameter(boolean... args){
paramList = new ArrayList<>();
for (boolean arg : args) {
paramList.add(arg);
}
}
public ArrayList<Boolean> getParamList(){return paramList;}
}
As you say, you want to add the rest of your split[]-tab to a Rapport-instance.
Just provide a String-Array as an attribute in your Rapport-class:
public class Rapport {
//some attributes...
private String[] eitherColumns;
//getters and setters and so on....
}
Then, when you read a line from the file, use a setter or another constructor to add the rest of the tabs to your Rapport-Instance.
You can also use a separate constructor which only gets the String-Array read from the file and let the class decide what to do with the array-elements which would be more convenient so separate your business logic from your data.

Jackson won't wrap class

I have a class that holds contact data; wrapped in a respective class. I recently changed my Photo setup from being a simple byte[] to being a wrapped class as well, but the instantitaion is a little different and now won't serialize/wrap properly.
My other classes wrap properly such as "number":{"log.PhoneNumber":{"number":"123-456-7890"}} but if I feed in a new photo (ie: new Photo("DEADBEEF")) I just get "photo":"DEADBEEF". This is causing problems with the deserializer too.
public class ContactInfo {
#JsonProperty("name") private Name m_name = null;
#JsonProperty("number") private PhoneNumber m_number = null;
#JsonProperty("email") private Email m_email = null;
#JsonProperty("photo") private Photo m_photo = null;
#JsonCreator
public ContactInfo(#JsonProperty("name") Name name,
#JsonProperty("number") PhoneNumber number,
#JsonProperty("email") Email email,
#JsonProperty("photo") Photo photo) {
/** Set vars **/
}
#JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_OBJECT)
static public class Photo {
private byte[] m_decodedBase64 = null;
public Photo(byte[] encodedBase64) {
m_decodedBase64 = Base64.decodeBase64(encodedBase64);
}
#JsonCreator
public Photo(#JsonProperty("photoData")String encodedBase64) {
m_decodedBase64 = Base64.decodeBase64(encodedBase64);
}
#JsonProperty("photoData")
public String getEncodedPhoto() {
return Base64.encodeBase64String(m_decodedBase64);
}
public byte[] getDecodedData() {
return m_decodedBase64;
}
}
}
What am I doing wrong?
Just figured out what it was. In the ContactInfo class there was a simple accessor function to get the encodedData.
public String getPhoto() {
return m_photo.getEncodedPhoto();
}
By simple putting it on ignore (or simply change it to return the object itself, which I might do),
#JsonIgnore
public String getPhoto() {
return m_photo.getEncodedPhoto();
}
The serializer stopped trying to read from it. I wish there was a way to set the serializer engine to be more "explicit declaration" for properties instead of "serialize everything that seems to match the member variables."

Constants and simplifying enum value access

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();

Reduce memory foot print by replace anonymous class with singleton. But need to refactor the design more

So I have this method that get executed repeatedly
public static boolean isReady(String dirPath, int numPdfInPrintJob){
File dir = new File(dirPath);
String[] fileList = dir.list(new FilenameFilter(){
public boolean accept(File file, String filename) {
return (filename.toLowerCase().endsWith(".pdf"));
}
});
if(fileList.length >= numPdfInPrintJob) return true;
else return false;
}
This method using anonymous class that will create a new instance of FilenameFilter every time invoked and I invoke this method a lot. So I want to make this anonymous class into a singleton. So my initial thought is to create a new singleton class that look like this
public class PdfFileNameFilter implements FilenameFilter{
private PdfFileNameFilter(){} //non-instantible
//guarantee to only have one instance at all time
public static final PdfFileNameFilter INSTANCE = new PdfFileNameFilter();
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".pdf"));
}
}
Can I refactor this a bit more. I need to do ZipFileNameFilter as well, and maybe many different file extension filter. Dont want to create a class for each filter. I need to refactor this design a bit more. Maybe interface come into place somewhere here.
If all you wanted to do was reduce memory usage you could have done
private static final FilenameFilter PDF_FILES = new FilenameFilter(){
public boolean accept(File file, String filename) {
return (filename.toLowerCase().endsWith(".pdf"));
}
}
If you want to create a singleton, the simplest way is
public enum PdfFileNameFilter implements FilenameFilter {
INSTANCE;
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".pdf"));
}
}
It seems simpler to me to just use your existing anonymous class and make one instance that all your method invocations use.
private static final FilenameFilter PDF_FILTER = new FilenameFilter() {
public boolean accept(File file, String filename) {
return (filename.toLowerCase().endsWith(".pdf"));
}
}
public static boolean isReady(String dirPath, int numPdfInPrintJob){
File dir = new File(dirPath);
String[] fileList = dir.list(pdfFilter);
if(fileList.length >= numPdfInPrintJob) return true;
else return false;
}
This is a case where subclassing and making a singleton seems to be a tad overkill: you simply want only one instance to use right here, whereas a singleton is used when there is only one instance you will ever want to use.
You could use an enum for this:
public enum ExtensionFilter implements FilenameFilter {
PDF(".pdf"),
ZIP(".zip");
private final String extension;
private ExtensionFilter(String extension) {
this.extension = extension;
}
#Override
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(extension));
}
}
Now you'll be able to use it like:
dir.list(ExtensionFilter.PDF)
You can also iterate through them if you need:
for ( FilenameFilter fileNameFilter : ExtensionFilter.values() ) {
....
}
You could also use a vararg as constructor argument to allow multiple extensions for the same filter and use the constant name as the default to make it simpler to use:
public enum ExtensionFilter implements FilenameFilter {
PDF,
ZIP(".zip", ".jar", ".war", ".ear");
private final String[] extensions;
private ExtensionFilter(String... extensions) {
if (extensions.length == 0) {
extensions = new String[] {"." + name().toLowerCase()};
}
this.extensions = extensions;
}
#Override
public boolean accept(File dir, String name) {
for (String extension : extensions) {
if (name.toLowerCase().endsWith(extension)) {
return true;
}
}
return false;
}
}
you can step away from the full singleton and use a private field to set up the extension
public class ExtensionFileNameFilter implements FilenameFilter{
private String extension;
private ExtensionFileNameFilter (String extension){this.extension=extension;}
public static final ExtensionFileNameFilter PDFINSTANCE = new ExtensionFileNameFilter (".pdf");
public static final ExtensionFileNameFilter ZIPINSTANCE = new ExtensionFileNameFilter (".zip");
//add instances as you need
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(extension));
}
}
Can I refactor this a bit more.
Yes, yes you can.
Assuming that wasn't the answer you were looking for (you should update yoru question to ask a more specific question), I wouldn't refactor it until you need it; YAGNI.
Once you have more code, like more FilenameFilters, is when you will see the possible refactorings. You will see common code, maybe an interface, stuff like that. Don't try to pre-maturely over-engineeer.
TDD is also the best way to do refactoring safely. If you have tests showing what code you actually need, lots of the extra stuff goes away, if any. And with a comprehensive test suite, you can refactor without hesitation because you know if your changes work or not based on whether your tests continue to pass.
For interest, this alternative accept implementation would run much faster in a benchmark test. It does not create new stateful Objects or carry the other overhead of String.toLowerCase, which is not required for your case.
public boolean accept(File file, String filename) {
int offset = s.length() - 4;
if (offset >= 0) {
if (s.charAt(offset) == '.') {
offset += 1;
if (s.regionMatches(offset, "pdf", 0, 3)) {
return true;
} else if (s.regionMatches(offset, "PDF", 0, 3)) {
return true;
}
}
}
return false;
}
If this was an execution hotspot and you were looking for optimization, something like that would help.

Categories