using abstract class methods - java

I am having some issues understand something about abstract classes. I have an abstract class created with a method for setting/getting a name:
public abstract class Person {
private String firstName;
public void setFirstname(String name)
{
this.firstName = name;
}
public String getFirstName()
{
return firstname;
}
I have 2 other classes that extend this class and they are Worker and Employer. I have the different workers and employers being created and stored into arraylists in a forth class called WorkPlace. My question is if I wanted to get every first name of every Person is there a way to do it without going through each arraylist and doing something like workerList.get.getFirstName?

Not unless your abstract class statically keeps a record of all first names, and you provide a static method to get all of those names.

Perhaps you could create a class like Workplace, and whenever you want a new Employer or Worker you would call something to the effect of workplaceName.newWorker(), which would return a new Worker and at the same time keep track of all Workers, so that you can later call workplaceName.getAllWorkerNames().

What you could do is override the toString() method in your Person class and return the first name.
When you then get out of ArrayList.toString() is a something like [firstname1, firstname2, firstname3]. You can remove the leading and trailing brackets and split the string with with ", ". This is a bit complicated and may decrease performance because of unnecessary string operations, but you would not required to loop over all elements in the collection.
Here is an example:
// in Person class do this
#Override
public String toString() {
return this.firstName;
}
// in your WorkPlace class do this
public String[] getAllFirstNames() {
String allNames = workerList.toString();
allNames = allNames.substring(1, allNames.length - 2);
return allNames.split(", ");
}
This should work and there is no loop (better: no loop you've written by your own). But as mentioned before, it may cost to much. In addition it may be not very save, in case the behavior of the collection's toString() method changes.

so if you have:
ArrayList<Person> list = new ArrayList<Person>();
Then:
Employer e1 = new Employer();
Worder w1 = new Worker();
list.add(e1);
list.add(w1);
Then you have to do:
list.get(0).getFirstName();

Related

Java: Why does my List contain different types? (Filtering Lists)

The whole time I thought, if I am using a List like List<Thing> things = new ArrayList<>() all items in this list are of Type Thing. Yesterday i was taught the other way.
I've created the following stuff and wonder why it is like it is.
An Interface Thing
public interface Thing {
String getType();
String getName();
}
A class ObjectA
public class ObjectA implements Thing {
private static final String TYPE = "Object A";
private String name;
public ObjectA(String name) {
this.name = name;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("ObjectA{");
sb.append("name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
#Override
public String getType() {
return TYPE;
}
#Override
public String getName() {
return name;
}
// equals and hashCode + getter and setter
}
A class ObjectB
public class ObjectB implements Thing {
private static final String TYPE = "Object B";
private String name;
private int value1;
private String value2;
private boolean value3;
public ObjectB(String name, int value1, String value2, boolean value3) {
this.name = name;
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
#Override
public String getType() {
return TYPE;
}
#Override
public String getName() {
return name;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("ObjectB{");
sb.append("name='").append(name).append('\'');
sb.append(", value1=").append(value1);
sb.append(", value2='").append(value2).append('\'');
sb.append(", value3=").append(value3);
sb.append('}');
return sb.toString();
}
// equals and hashCode + getter and setter
}
The main method
public static void main(String[] args) {
final List<Thing> things = new ArrayList<>();
final ObjectA objA = new ObjectA("Thing 1");
final ObjectB objB = new ObjectB("Thing 2", 123, "extra", true);
things.add(objA);
things.add(objB);
// The List doesn't contain Thing entities, it contains ObjectA and ObjectB entities
System.out.println(things);
for(final Thing thing : things) {
if (thing instanceof ObjectA) {
System.out.println("Found Object A: " + thing);
final ObjectA object = (ObjectA) thing;
}
if (thing instanceof ObjectB) {
System.out.println("Found Object B: " + thing);
}
}
}
The output of this method is:
[ObjectA{name='Thing 1'}, ObjectB{name='Thing 2', value1=123, value2='extra', value3=true}]
So i assume i've ObjectA entities and ObjectB entities in my List<Thing>.
Question: Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?
additional Question: I've started to filter this List<Thing> with instanceof but i have read instanceof and casting are bad practice (e.g. no good model design). Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?
You should avoid instanceof check in your additional question example. When you work with List items, it should be sufficient to have interface methods available. If you need to do something with only ObjectA or ObjectB, I suggest to use another List with only ObjectA or ObjectB. For example you can define different methods to do Thing specific job and ObjectB specific job:
public void processThings(List<Thing> things) {
for(final Thing thing : things) {
// we work only with methods that provided by interface Thing
System.out.println(thing.getType());
System.out.println(thing.getName());
}
}
public void processObjectsB(List<ObjectB> objectsB) {
// here we do some specific things with only B objects,
// assuming class ObjectB has an additional method doSomeSpecificB()
for(final ObjectB objectB : objectsB) {
objectB.doSomeSpecificB();
}
}
I have a garden that contains potatoes, carrots and broccoli. I have a very strict rule - I won't plant anything in the garden that I can't eat. So no poison ivy here!
So this is a Garden<Edible> - everything I plant in the garden has to be edible.
Now class Potato implements Edible means that every potato is edible. But it also means that I can plant a potato in my garden. Likewise, class Carrot implements Edible - all carrots are edible, and I'm allowed to plant carrots.
It's a dark night, and I'm hungry. I go out to my garden and put my hand on something in the garden. I can't see what it is, but I know that everything in my garden is edible. So I pull it out of the garden, and take it inside to cook and eat. It doesn't matter what I've grabbed - I know it will be something I can eat.
Because this is a Garden<Edible>. It may or may not contain Potato objects. It may or may not contain Broccoli objects. It does not contain PoisonIvy objects.
Now, translate that all to your example. You have class ObjectA implements Thing - which means that every ObjectA is a Thing. You have class ObjectB implements Thing - which means that every ObjectB is a Thing. And you have a List<Thing> - a List that can contain ObjectA objects, ObjectB objects, and any other object of any class that implements Thing. What you can't put in it is an object of any class that doesn't implement Thing.
Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?
This behaviour is called "polymorphism". Basically, since ObjectA and ObjectB implements Thing, instances of ObjectA and ObjectB can be used like a Thing object. In your code, you added them to a list that can contain Thing objects.
Note how even if those objects are now of (compile time) type Thing, at runtime they still know what they are. When you call toString on them, the respective overridden toString methods in ObjectA and ObjectB will be called. It is as if the Thing "morphs" into ObjectA or ObjectB.
Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?
The reason why people say this is bad practice is because if you want to do different things depending whether the object is ObjectA or ObjectB, why did you make them implement Thing and make a list of Thing to store them? You could have just used a List<Object>. The real advantage of using List<Thing> is that you avoid knowing what actual objects are in there when you are working with the list. All you know is that the things inside the list implement Thing and you can call methods declared in Thing.
So if you need to filter the list to separate the two types, you could have just created two lists to store them in the first place. One for ObjectA and one for ObjectB. Obviously, this is not always possible, especially if the list comes from somewhere else (like a external library). In that case, your current code is fine.
things is a List<Thing>. That means that at compile time, Java will ensure that any object that you write to things is a Thing. As ObjectA and ObjectB both implement Thing the actual implementation of any member of things can be ObjectA or ObjectB. This is by design and that feature is called polymorphism: object of different classes share a common interface and can be accessed thrrough that interface independently of their actual type. For example you could use:
for(final Thing thing : things) {
System.stdout.println("Found a " + thing.getType() + " named " + thing.getName());
}
Using instanceof and casting is not necessarily bad practice and can have correct use case. But it is often a hint that the hierachy of classes and interfaces has not been properly designed. Ideally, if you have to process a Thing, you should not wonder about its actual class: you have a Thing, and using Thing methods should be enough.
In that sense, instanceof is at the same level as reflection is: it is a low level tool that allows to see what is hidden under the hood. And any time you use it, you should ask you whether polymorphism could not be enough.

Calling a Constructor without parameters

I am trying to build a class with a constructor, mutators and accessors. Reading through books and online, I am made to learn that you can call a constructor with or without parameters. However, my case below seems not to work. I am not even able to compile without errors. It works when I use student jane = new student(""). What am I doing wrong?
Devolution.java:6: cannot find symbol
symbol : constructor student()
location: class Governor
student jane = new student();
^
public class designers {
public static void main(String[] args) {
student jane = new student();
student smith = new student("jane", "36365355", "Nairobi", "Male");
System.out.println("Janes's Properties: "+jane.name() + " " + jane.nationalID() + " " + jane.county()+" "+jane.gender());
System.out.println("Smith's Properties: "+smith.name() + " " + smith.nationalID() + " " + smith.county()+" "+smith.gender());
}
}
other code is below
public class student {
//Private fields
private String name;
private String nationalID;
private String county;
private String gender;
//Constructor method
public student(String name, String nationalID, String county, String gender)
{
this.name = name;
this.nationalID = nationalID;
this.county = county;
this.gender = gender;
}
//Accessor for name
public String name()
{
return name;
}
//Accessor for nationalID
public String nationalID()
{
return nationalID;
}
//Accessor for county
public String county()
{
return county;
}
//Accessor for gender
public String gender()
{
return gender;
}
}
A constructor is a way of creating an instance of a class:
Student s = new Student(...);
will create a new instance of the Student class and enable you to access it using s.
Often, when you create an instance of a class, you need to specify certain information that's used in building the instance. In the case of a student, that might be the name, the age, and so on. You'd have a constructor that looks like this:
public Student(String name, int age) {
//...
}
But in some contexts, you can build an instance of a class without needing (at least initially) to specify anything. So you might, for instance, have a constructor like this
public Student() {
//...
}
which leaves the name and age fields blank or zeroed out, until you later set them with another method of the class.
The critical point for what you're doing is that you've made a constructor that requires various parameters, but you haven't specified one like this second example that doesn't require any. As things stand, you can write
Student s = new Student("Bob", "ABC12345", "Surrey", "Male");
because you've got a constructor that takes four Strings as arguments. But you can't write
Student s = new Student();
because you didn't create a constructor that takes no arguments.
The slight wrinkle in this is that if you don't specify any constructors in your class, then Java will automatically create one for you that takes no arguments and doesn't do anything special. So if you don't write any constructors, you'll get one for free that looks like this:
public Student() {
}
But that's only if you don't write any of your own. Since you've specified one that does take parameters, Java won't give you a no-argument one for free. You have to put it in yourself if you want to be able to create instances without any arguments.
You've only written one constructor - the one with four parameters. You don't have a constructor without parameters, so you can't write new student().
Note that if you don't write any constructors at all, the compiler will automatically make a constructor for you, without parameters, but as soon as you write one constructor, this doesn't happen.
By the way, most people use capital letters for class names (so Student, not student). This makes it easy to distinguish them from the names of other identifiers. It would be good for you to get into the habit of doing the same.
You don't have a constuctor without parameters in the student class. Such a constructor is generated by the compiler only if you haven't defined any other constructors, which you have.
Just add the constructor :
public student()
{
this.name = null;
this.nationalID = null;
this.county = null;
this.gender = null;
}
You need to make another constructor as follow:
public Student(){
//do things here
}
Explanation:
When no constructors are defined in a class then there is a default constructor(without
any parameters) already. In which case you don't need to define it. But if you have any constructor with some parameters, then you need to define the constructor without parameters as well.
Its called overloading the constructor. In your class, declare a constructor again without parameter requirements. See this post for more info
You don't have a constructor without parameters. That would only be the case when you had not write an own one. When you want to have the possibility to make an object of the class with or without parameters, you need two different constructors in your code.

How can we get immutable object if class did not implement cloneable

I have few issues/doubts to fill values in a HashMap
I want a HashMap to accept "Student" as key and "Details" as value.
Since key to a hashMap should be immutable I have some doubts how can this be dealt if
Student class did not cloneable
Student class has reference to which in turn have reference to "Lab"
public class Student {
private String id;
private String name;
private Department dept;
public Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
public Department getDepartment()
{
return this.dept;
}
}
public class Department {
private String deptId;
private Lab lab;
public Department(String deptId, Lab lab)
{
this.deptId=deptId;
this.lab=lab;
}
public void setLab(Lab lab)
{
this.lab=lab;
}
}
public class Lab {
private String labId;
private String labName;
public Lab(String labId, String labName)
{
this.labId=labId;
this.labName=labName;
}
}
public class StudentDetails
{
private String fatherName;
private String address
public StudentDetails(String fatherName, String address)
{
this.fatherName=fatherName;
this.address=address;
}
}
public class StudentMaintainer {
public static void main(String[] args)
{
StudentDetails stDetails= new StudentDetails("John","Mumbai");
Lab lab= new Lab("100","CS");
Department dept= new Department("900", lab);
Student st = new Student("3000",dept);
Map<Student,StudentDetails> studentMaintainer= new ArrayList<>();
studentMaintainer.put(st,stDetails);
}
}
Now Even if Student is cloneable, I can get reference of Department and call setLab() which changes the StudentObject. (Am I wrong?)
Now if Department and Lab are from 3rd party jars, how can I use Student Object in my Map if Student hashCode is (primeNumber+Student.id+Department.id+Lab.id).hashcode() [just some weird case];
Immutability has nothing to do with Cloneable as far as I understand it, and in fact just the opposite. Immutability has more to do with declaring the class final and using immutable fields, non-overridable methods, no setter methods, getter methods that return deep copies of fields or immutable fields, etc... Please read A Strategy for Defining Immutable Objects for more on this.
Also your code has a pseudo-constructor:
public void Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
A true constructor should not be declared to return anything, not even void. Better would be:
// note the difference?
public Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
Also, your Student class should properly override equals and hashCode if it is to work well as a key for a HashMap.
Now Even if Student is cloneable, I can get reference of Department
and call setLab() which changes the StudentObject. (Am I wrong?)
You are correct. This can happen, and can cause your Student class to appear to be mutated. For an instance of Student to be immutable, you must not be able to modify any of its fields[0]. That includes calling something like a setter method on one of its fields.
Now if Department and Lab are from 3rd party jars, how can I use
Student Object in my Map if Student hashCode is
(primeNumber+Student.id+Department.id+Lab.id).hashcode() [just some
weird case];
That is a very good question. You obviously can't just change the classes to be immutable, since you don't have control over them, so you may have to get a bit creative. Possible solutions:
if the third party objects you want to use are interfaces, you could implement the interface with your own type, where the body of every mutator method throws an exception (think e.g. java.util.Collections.unmodfiableList). This has the benefits that you can still refer to the third party class in your codebase, but the drawback that calling mutator methods will fail at runtime, rather than at compile time.
write adapters in your own codebase, like this:
public final class MyImmutableDepartment {
private final MyImmutableLab lab;
private final String departmentId;
public MyImmutableDepartment(Department thirdPartyMutableDepartment) {
this.departmentId = thirdPartyMutableDepartment.getId();
this.lab = new MyImmutableLab(thirdPartyMutableDepartment.getLab());
}
// getters and the MyImmutableLab class left as an exercise
}
This has the advantage that you know at compile time, the classes cannot be mutated.
The downside in both approaches is that you basically have to mirror every class from the third party library, to ensure they're immutable.
I don't think there's any other alternatives.
[0] there are some cases where this is possible, and can be used for internal caching, but it's a decent guideline to stick to when learning.
Student does not need to be immutable! Specifically, the requirement is that the behavior of equals/hashCode doesn't change when the key is in the HashMap.
This can be accomplished in three ways:
Don't implement equals/hashCode. If you use the default reference equality, it doesn't matter how you mutate the key. To clarify intent, override those methods, explicitly call super.equals, and make them final.
Don't include any fields that will mutate in your calculation of equals/hashCode. Or, if the properties of a field may change but not the reference, use reference equality (==) instead of field.equals() and call System.identityHashCode(field) instead of field.hashCode()
Don't mutate the object while it is used as a key in the HashMap. A bit dangerous, but works fine if references aren't held by code beyond your control. Document the requirement.
But, in your concrete example, each student has an id. Why would you use any other properties when implementing equals/hashCode.?

Java arraylist indexof

Here is my class animals.java:
public class animals {
String Name, ID;
static ArrayList<animals> animalData = new ArrayList<animals>();
public animals(){}
public animals(String name, String id){
super();
this.Name = name;
this.ID = id;
}
public void addAnimal(String name, String id){
animalData.add(new animals(name, id));
}
public int search(String name){
return this.animalData.indexOf(name);
}
}
When I add an animal name with an id it works normally, but when I use search method I saw only -1. Maybe I try override method equals or indexof in this class? help me for this
Thank you and sorry for my bad english..
You are adding instances of animals to the list. You are searching for the instance by name. Since animalData does not contain any instances of String, indexOf() will never return an index.
If you want to access an instance of animals by the name, you should use a Map<String,animals>.
Map<String,animals> animalMap = new HashMap<String,animals>();
animalMap.put("Mat", new animals("Mat", "id");
animals animal = animalMap.get("Mat");
The proper use of indexOf() is to pass in an instance that is equal to an instance already in the collection. As others have pointed out, this will require you to define equals() to define what makes two instances equal. You should also override hashcode() when you override equals() because there is an assumed correlation.
Note: The convention is to use CapitalCase for class names. Also, the class name should not be plural. You will have many instances of Animal, you may later make a class that is a collection of Aniamals, but that should not be the name of the main class.
Yes, you need to override equals() and hashcode() methods when you use objects in collections and perform lookup based on object.
indexOf() returns object because it just returns object at that perticular index. But, when you do object based lookup, if equals() and hashCode() are not overridden, equals() may fail and you get unpredictable results.
You need to define an "equals" method
You're looking for a String...
You'd better use a HashMap I think...
But yeah you have to change your structure(which isn't very efficient)
Here is the code I would use:
public class animals {
String Name, ID;
static Map<String, animals> animalData = new HashMap<String, animals>();
public animals(){}
public animals(String name, String id){
super();
this.Name = name;
this.ID = id;
}
public static void addAnimal(String name, String id){
animalData.add(new animals(name, id));
}
// Returns null if no registered animal has this name.
public animals search(String name){
return this.animalData.get(name);
}
}
This way, you make the search method much faster (O(1)), you don't need to override the equals method anymore.
Note that if animalData is static, you should consider to make addAnimal() static as well since it's sort of a 'setter' for aniamalData.

What would be the best way to implement a constant object?

First of all I should probably say that the term 'constant object' is probably not quite right and might already mean something completely different from what I am thinking of, but it is the best term I can think of to describe what I am talking about.
So basically I am designing an application and I have come across something that seems like there is probably an existing design pattern for but I don't know what it is or what to search for, so I am going to describe what it is I am trying to do and I am looking for suggestions as to the best way to implement it.
Lets say you have a class:
public class MyClass {
private String name;
private String description;
private int value;
public MyClass(String name, String description, int value) {
this.name = name;
this.description = description;
this.value = value;
}
// And I guess some getters and setters here.
}
Now lets say that you know in advance that there will only ever be say 3 instances of this class, and the data is also known in advance (or at least will be read from a file at runtime, and the exact filename is known in advance). Basically what I am getting at is that the data is not going to be changed during runtime (once it has been set).
At first I thought that I should declare some static constants somewhere, e.g.
public static final String INSTANCE_1_DATA_FILE = "path/to/instance1/file";
public static final String INSTANCE_2_DATA_FILE = "path/to/instance2/file";
public static final String INSTANCE_3_DATA_FILE = "path/to/instance3/file";
public static final MyClass INSTANCE_1 = new MyClass(getNameFromFile(INSTANCE_1_DATA_FILE), getDescriptionFromFile(INSTANCE_1_DATA_FILE), getValueFromFile(INSTANCE_1_DATA_FILE));
public static final MyClass INSTANCE_2 = new MyClass(getNameFromFile(INSTANCE_2_DATA_FILE), getDescriptionFromFile(INSTANCE_2_DATA_FILE), getValueFromFile(INSTANCE_2_DATA_FILE));
public static final MyClass INSTANCE_3 = new MyClass(getNameFromFile(INSTANCE_3_DATA_FILE), getDescriptionFromFile(INSTANCE_3_DATA_FILE), getValueFromFile(INSTANCE_3_DATA_FILE));
Obvisouly now, whenever I want to use one of the 3 instances I can just refer directly to the constants.
But I started thinking that there might be a cleaner way to handle this and the next thing I thought about was doing something like:
public MyClassInstance1 extends MyClass {
private static final String FILE_NAME = "path/to/instance1/file";
public String getName() {
if (name == null) {
name = getNameFromFile(FILE_NAME);
}
return name;
}
// etc.
}
Now whenever I want to use the instances of MyClass I can just use the one I want e.g.
private MyClass myInstance = new MyClassInstance2();
Or probably even better would be to make them singletons and just do:
private MyClass myInstance = MyClassInstance3.getInstance();
But I can't help but think that this is also not the right way to handle this situation. Am I overthinking the problem? Should I just have a switch statement somewhere e.g.
public class MyClass {
public enum Instance { ONE, TWO, THREE }
public static String getName(Instance instance) {
switch(instance) {
case ONE:
return getNameFromFile(INSTANCE_1_DATA_FILE);
break;
case TWO:
etc.
}
}
}
Can anyone tell me the best way to implement this? Note that I have written the sample code in Java because that is my strongest language, but I will probably be implementing the application in C++, so at the moment I am more looking for language independent design patterns (or just for someone to tell me to go with one of the simple solutions I have already mentioned).
If you want the values to be constant, then you will not need setters, otherwise code can simply change the values in your constants, making them not very constant. In C++, you can just declare the instances const, although I'd still get rid of the setters, since someone could always cast away the const.
The pattern looks ok, although the fact that you are creating a new instance each time one is requested, is not usual for constants.
In java, you can create enums that are "smart" e.g.
public enum MyClass {
ONE(INSTANCE_1_DATA_FILE),
TWO(INSTANCE_2_DATA_FILE),
//etc...
private MyClass(String dataFile)
{
this(getNameFromDataFile(dataFile), other values...)
}
private MyClass(String name, String data, etc...)
{
this.name = name;
// etc..
}
public String getName()
{
return name;
}
}
In C++, you would create your MyClass, with a private constructor that takes the filename and whatever else it needs to initialize, and create static const members in MyClass for each instance, with the values assigned a new instance of MyClass created using the private constructor.
EDIT: But now I see the scenario I don't think this is a good idea having static values. If the types of ActivityLevel are fundamental to your application, then you can enumerate the different type of activity level as constants, e.g. a java or string enum, but they are just placeholders. The actual ActivityDescription instances should come from a data access layer or provider of some kind.
e.g.
enum ActivityLevel { LOW, MED, HIGH }
class ActivityDescription
{
String name;
String otherDetails;
String description; // etc..
// perhaps also
// ActivityLevel activityLevel;
// constructor and getters
// this is an immutable value object
}
interface ActivityDescriptionProvider
{
ActivityDescription getDescription(ActivityLevel activityLevel);
}
You can implement the provider using statics if you want, or an enum of ActivityDescription instnaces, or better still a Map of ActivityLevel to ActivityDescription that you load from a file, fetch from spring config etc. The main point is that using an interface to fetch the actual description for a given ActivityLevel decouples your application code from the mechanics of how those descriptions are produced in the system. It also makes it possible to mock the implementation of the interface when testing the UI. You can stress the UI with a mock implementation in ways that is not possible with a fixed static data set.
Now lets say that you know in advance that there will only ever be say 3 instances of this class, and the data is also known in advance (or at least will be read from a file at runtime, and the exact filename is known in advance). Basically what I am getting at is that the data is not going to be changed during runtime (once it has been set).
I'd use an enum. And then rather in this flavor:
public enum MyEnum {
ONE("path/to/instance1/file"),
TWO("path/to/instance2/file"),
THREE("path/to/instance3/file");
private String name;
private MyEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Which can be used as follows:
MyEnum one = MyEnum.ONE;
String name = one.getName();
(I'm too slow once again, you already accepted an answer, but here it is anyway...)
You want to (a) prevent changes to the data held in objects of MyClass, and (b) allow only a fixed set of MyClass objects to exist, implying that runtime code should not be able to create new instances of MyClass.
Your initial example has a public constructor, which violates (b)
I'd use a Factory approach so the Factory is the only thing that can create instances, and the class doesn't provide any setters so it's immutable.
Depending on how much flexibility you want for the future, you could put the factory and the class in the same package and limit scope that way, or you could make MyClass an inner class within the factory. You may also consider making MyClass an interface separate from its implementation.
A properties file could be used to configure the factory itself.
The properties file (e.g. "foo.properties") could look something like
one=/path/to/datafile1
two=/another/path/to/datafile2
three=/path/to/datafile3
I use "Foo" instead of "MyClass" in the (Java) examples below.
public class FooFactory
{
/** A place to hold the only existing instances of the class */
private final Map<String, Foo> instances = new HashMap<String, Foo>();
/** Creates a factory to manufacture Foo objects */
// I'm using 'configFile' as the name of a properties file,
// but this could use a Properties object, or a File object.
public FooFactory(String configfile)
{
Properties p = new Properties();
InputStream in = this.getClass().getResourceAsStream();
p.load(in); // ignoring the fact that IOExceptions can be thrown
// Create all the objects as specified in the factory properties
for (String key : p.keys())
{
String datafile = p.getProperty(key);
Foo obj = new Foo(datafile);
instances.put(key, obj);
}
}
public Foo getFoo(String which)
{
return instances.get(which);
}
/** The objects handed out by the factory - your "MyClass" */
public class Foo
{
private String name;
private String description;
private int value;
private Foo(String datafile)
{
// read the datafile to set name, description, and value
}
}
}
You're set to allow only your predefined instances, which can't be changed at runtime, but you can set it all up differently for another run at a later time.
Your first method seems to me like the best and the least prone to code rot. I'm not impressed by the idea of subclassing an object just to change the file name that contains the data that will be used to build it.
Of course, you could maybe improve on your original idea by wrapping these all in an outer class that provides some sort of enumeration access. A collection of MyClass's in other words. But I think you should discard this subclassing idea.
First, you really should be limiting where you use these instances in the code. Use them in as few places as possible. Given these are file names, I expect you want three class instances which accesses the files. How many classes are required depends on what your want to do with them? Look at the Singleton pattern for these classes.
Now you don't need the constants, but could have a helper class which will read the file containing the file names and supply them to the reader class. The code to find then name could also be a method called by the static initializer of the Singleton.
The common approach is to use a map:
private static final Map<String, YouClass> mapIt =
new HashMap<String, YouClass>(){{
put("one", new YourClass("/name", "desc", 1 )),
put("two", new YourClass("/name/two", "desc2", 2 )),
put("three", new YourClass("/name/three", "desc", 3 ))
}}
public static YourClass getInstance( String named ) {
return mapIt.get( named );
}
Next time you need it:
YouClass toUse = YourClass.getInstance("one");
Probably using strings as keys is not the best option but you get the idea.

Categories