I'm a student studying software development (1st year) and the teaching language we are using is Java. We have covered basics, and most of OOP, but I've been practicing making a Shop Administration System and I've come up against something I can't reckon with.
I'm trying to unit test two classes which are both abstract superclasses of several other classes I plan on implementing, as per the UML below
Person Superclass and Employee subclass - both abstract
I've read through a series of posts on here and I see a lot of people were recommending things like power mock and mockito for making mock objects. I'm probably trying to learn too much at once as it is but basically I landed on concrete "wrapper" private classes in the unit test class that i used to polymorphically create the Employee objects (technically EmployeeWrapper objects), then unit testing all the public methods through the wrapper class.
I'm vaugely familiar with the term "bad code smell" and this really stinks. Is there a standard way of testing abstract superclasses without using things like Mockito and Power Mock? Or do i just need to suck it up and use things like that?
This is the code for the classes (with all method bodies removed so you dont have to read through a load of unimportant details
import java.time.LocalDateTime;
import java.util.Hashtable;
import java.util.Iterator;
public abstract class Employee extends Person {
private double hourlyRate;
private double hoursPerWeek;
private LocalDateTime dateOfEmploymentStart;
private LocalDateTime dateOfEmploymentEnd;
private Hashtable<LocalDateTime, Integer> shifts;
private static final double MINIMUM_WAGE = 8.0;
/**
* Constructor for Employee for all fields except dateOfHire, which is set to {#code LocalDateTime.now()}
*
* #param name
* #param email
* #param phoneNumber
* #param hourlyRate
* #param weeklyHours
* #throws IllegalArgumentException if name if blank or null
*/
public Employee(String name, String email, String phoneNumber, double hourlyRate, double weeklyHours) throws IllegalArgumentException {
super(name, email, phoneNumber);
this.setHourlyRate(hourlyRate);
this.setWeeklyHours(weeklyHours);
this.setDateOfEmploymentStart(LocalDateTime.now());
this.shifts = new Hashtable<LocalDateTime, Integer>();
}
/**
* Constructor for Employee that sets name, email and phoneNumber to provided args; and sets hourly rate and weeklyHours to 0
*
* #param name
* #param email
* #param phoneNumber
* #throws IllegalArgumentException if name is blank or null
*/
public Employee(String name, String email, String phoneNumber) throws IllegalArgumentException {
this(name, email, phoneNumber, MINIMUM_WAGE, 0);
}
/**
* Constructor for Employee that sets only name
*
* #param name
* #throws IllegalArgumentException
*/
public Employee(String name) throws IllegalArgumentException {
this(name, null, null);
}
}
and the Unit test class (with all test cases bar one removed, and that one method body is left empty - again to stop clutter:
import static org.junit.jupiter.api.Assertions.*;
import java.time.LocalDateTime;
import java.util.Hashtable;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class EmployeeTest {
private class EmployeeWrapper extends Employee {
public EmployeeWrapper(String name, String email, String phoneNumber, double hourlyRate, double weeklyHours) throws IllegalArgumentException {
super(name, email, phoneNumber, hourlyRate, weeklyHours);
}
public EmployeeWrapper(String name, String email, String phoneNumber) throws IllegalArgumentException {
super(name, email, phoneNumber);
}
public EmployeeWrapper(String name) throws IllegalArgumentException {
super(name);
}
}
private String nameValid, emailValid, phoneNumberValid;
private String nameInvalid, emailInvalid, phoneNumberInvalid;
private double hourlyRateValid, hourlyRateInvalidLow;
private double weeklyHoursValid, weeklyHoursInvalid;
private final double DEFAULT_HOURLY_RATE = 8;
private final double DEFAULT_WEEKLY_HOURS = 0;
private final String DEFAULT_EMAIL = "no email provided";
private final String DEFAULT_PHONE_NUMBER = "no phone number provided";
private final double MINIMUM_WAGE = 8.0;
private Employee employee;
private Hashtable<LocalDateTime, Integer> shiftsValid, shiftsInvalidEmpty;
private LocalDateTime dateTimeValid, dateTimePast, dateTimeFuture;
#BeforeEach
void setUp() throws Exception {
// valid employee
nameValid = "testname";
phoneNumberValid = "123456789";
emailValid = "test#test.test.com";
hourlyRateValid = 10.50;
weeklyHoursValid = 7.5;
employee = new EmployeeWrapper(nameValid, emailValid, phoneNumberValid, hourlyRateValid, weeklyHoursValid);
// test data
nameInvalid = "";
emailInvalid = ".test#test.com";
phoneNumberInvalid = "";
hourlyRateInvalidLow = 5;
weeklyHoursInvalid = -10;
dateTimeValid = LocalDateTime.of(2015, 6, 15, 13, 30);
dateTimePast = LocalDateTime.MIN;
dateTimeFuture = LocalDateTime.MAX;
shiftsValid = new Hashtable<LocalDateTime, Integer>();
shiftsValid.put(dateTimeValid, 6);
shiftsValid.put(dateTimeFuture, 3);
shiftsInvalidEmpty = new Hashtable<LocalDateTime, Integer>();
}
#Test
void testEmployeeConstructorValidAllArgs() {
}
}
This is my first post of Stack Overflow so i apologise profusely if i have omitted any revelent details.
If you see any other stupid things i've done in the code I'll also gladly take any criticism!
edit: thanks everyone for the responses they have been amazing, i really really appreciate it!
First let me say, that your approach is absolutely viable. I am just sharing my own way of doing it, because it spares copy pasting tests common between different implementations.
I don't specifically test abstract classes. Because we are testing functionality and it can be overridden in subclasses. I'll use your Person class for this setup, but i will simplify it a bit.
public abstract class Person {
private String name;
private String email;
public Person(String name, String email) {
this.setName(name);
this.email = email;
}
public String getName() {
return this.name;
}
public void setName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("missing name");
}
this.name = name;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
Student
public class Student extends Person {
private String university;
public Student(String name, String email, String university) {
super(name, email);
this.university = university;
}
public String getUniversity() {
return this.university;
}
public void setUniversity(String university) {
this.university = university;
}
}
Child
public class Child extends Person {
private String school;
public Child(String name, String email, String school) {
super(name, email);
this.school = school;
}
public String getSchool() {
return this.school;
}
public void setSchool(String school) {
this.school = school;
}
#Override
public String getName() {
return "I am not saying!";
}
}
So we have the abstract Person, a Student, whose specific thing is a university and a Child. Having a school is what is specific for the child, but it also changes the behaviour of getName(), it does not disclose its name. This might be desired, but for this example we'll assume it was incorrect to override getName() like this.
When dealing with abstract classes i make an abstract test class, which holds common setup and tests for common functionality provided by the abstract class - Person in this case.
public abstract class PersonBaseTests {
protected static final String EXPECTED_NAME = "George";
private Person person;
#BeforeEach
public void setUp() {
this.person = getConcretePersonImplementation();
}
/**
* #return new instance of non-abstract class extending person
*/
protected abstract Person getConcretePersonImplementation();
//common tests
#Test
public void testGetName_ShouldReturnCorrectValue() {
assertEquals(EXPECTED_NAME, this.person.getName());
}
#Test
public void testConstructor_ShouldThrowIllegalArgumentExceptionOnMissingName() {
Executable invalidConstructorInvocation = getConstructorExecutableWithMissingName();
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, invalidConstructorInvocation);
assertEquals("missing name", exception.getMessage());
}
protected abstract Executable getConstructorExecutableWithMissingName();
//other common tests
}
The test classes extending the base must provide the concrete implementation to be tested. They will also inherit the tests, so you don't need to write them again. If you still have not learned about interfaces, lambdas and stuff like that, you can ignore the constructor exception test and everything related to it, and focus on getName() test. It tests that the getter correctly returns the name of the Person. This will obviously fail for Child, but that's the idea. You can add tests for getting and setting email, phone, etc.
So, student tests
public class StudentTests extends PersonBaseTests {
#Override
protected Person getConcretePersonImplementation() {
return new Student(PersonBaseTests.EXPECTED_NAME, "mail", "Cambridge");
}
#Override
protected Executable getConstructorExecutableWithMissingName() {
//setup invocation which will actually fail
return new StudentConstructorExecutable(null, "email#email.email", "Stanford");
}
private static final class StudentConstructorExecutable implements Executable {
private final String name;
private final String email;
private final String university;
private StudentConstructorExecutable(String name, String email, String university) {
this.name = name;
this.email = email;
this.university = university;
}
#Override
public void execute() throws Throwable {
//this will invoke the constructor with values from fields
new Student(this.name, this.email, this.university);
}
}
//write tests specific for student class
//getUniversity() tests for example
}
Again, ignore the Executable and everything related to the constructor test, if you have not learned it yet. Student tests provide concreete instance of Student for the common inherited tests, and you can write additional tests for specific functionality - get/set university.
Child tests
public class ChildTests extends PersonBaseTests {
#Override
protected Person getConcretePersonImplementation() {
return new Child(PersonBaseTests.EXPECTED_NAME, "", "some school");
}
#Override
protected Executable getConstructorExecutableWithMissingName() {
//this can be ignored
return () -> new Child(null, "", "");
}
//write tests specific for child class
//getSchool() tests for example
}
Again, a concrete instance is provided for the common tests - this time of type Child. And you can add tests for any additional functionality provided by Child class - get and set school in this example. Then you can write more test classes for every additional subclass of Person.
Like this you keep common tests at one place and every concrete implementation of abstract class you write is completely tested, without test duplication. About the failing test, if the change in behaviour of getName() is intentional, you can just override it in ChildTests to take that into account. If it is not intentional, you know, that Student.getName() is correct, while Child.getName() is not, but you wrote the test only once.
as you stated you can't test abstract classes in Java. You need a mocking framework like Mockito or a concrete class that extends your superclass, in this case Employee.
And that's what you have done with your class EmployeeWrapper. Except for the name (i would name it EmployeeImpl) i'm fine with your solution.
Related
Just a question RE: Constructor Chaining in subclasses that I can't find a good answer on and I'm confusing myself a bit with.
I'm making a basic little Text Based RPG for some practice and I'm going through my constructors for an abstract class and have the constructors from 0-4 params chained together like below
abstract class Creature {
// Fields
private String name;
private int lifeForce;
private int strength;
private int agility;
// Constructors + Chaining
public Creature() {
this("Unknown")
}
public Creature(String name) {
this(name, 100);
}
public Creature(String name, int lifeForce) {
this(name, lifeForce, 10);
}
public Creature(String name, int lifeForce, int strength) {
this(name, lifeForce, strength, 10);
}
public Creature(String name, int lifeForce, int strength, int agility) {
this.name = name;
this.lifeForce = lifeForce;
this.strength = strength;
this.agility = agility;
}
My confusion is how best to format the constructors of a subclass of creature, for example this simple Person class introduces two new fields. There's definitely too much repetition if I write the constructors like this
// Constructors + Chaining
public Person() {
super("Unknown");
this.skillClass=new Mage();
this.dialogue="...";
}
public Person(String name) {
super(name);
this.skillClass=new Mage();
this.dialogue="...";
} etc etc etc
I suppose I could restrict the constructors to limit the repetition but I'm mostly just wondering if there's good best practice that I'm missing here.
Any and all suggestions welcome and if anyone has any good resources to recommend that go deeper than the usual
Class B extends Class A
examples I'd massively appreciate.
In situations like this one when you need to use multiple constructors with different parameters, it is recommended to use the builder pattern like this :
abstract class Creature {
// Fields
private String name;
private int lifeForce;
private int strength;
private int agility;
private Creature(Builder<?> builder) {
this.name = builder.name;
this.lifeForce = builder.lifeForce;
// Add the other attributes here.
}
public static abstract Builder extends Builder<T extends Builder<T>> {
private String name;
private int lifeForce;
private int strength;
private int agility;
public Builder(//here you put the attributes that you need to have in all instances) {
// here you do the affectations.
}
// now you need to make the functions that set each property :
public Builder lifeForce(int lifeForce) {
this.lifeForce = lifeForce;
return this;
}
// you do the same thing for all the other attributes.
...
public Creature build() {
return new Creature(this);
}
}
}
So for the explanation : This pattern will allow you to create instances of your class by setting only the needed attributes.
As here you have subclasses the builder pattern will be little bit more harder to understand but it is the perfect solution in such situation.
We need to apply the builder pattern also for every subclasse so let's do it for the person class :
public class Person extends Creature {
private int anotherField;
public Person(Builder builder) {
super(builder);
this.anotherField = anotherField;
}
public static Builder extends Creature.Builder<Builder> {
public Builder(//add the fieldHere if it is needed in all class instances) {
// if the field is not mandatory you can omit this constructor but you need to put the function below.
}
public Builder anotherField(int anotherField) {
this.anotherField = anotherField;
}
public Person build() {
return new Person(this);
}
}
Now let me show you how tricky is this solution :
1/ declare person with 2 fields :
Person p1 = Person.Builder().name("name").anotherField(0).build();
2/ declare another one with just one field
Person p2 = Person.Builder().agility(1000).build();
Remark : In these two examples, i supposed that your builders' constructors don't have parameters. If for example the name is mandatory field :
Person p3 = Person.Builder("name").anotherField(0).build();
I wish that you had the idea about using builder pattern.
edit: added employee constructors;
my question involves constructor chaining for a subclass (to a super which is itself a subclass). I have written constructors that seem to work, but I feel that they are incorrectly written as I will explain. PTEmployee extends to Employee who is extended to a Person. The code below seems to work in my test class,
public class PartTimeEmployee extends Employee {
public static final int DEFAULT_HOURSPRWK = 0;
private int hoursPerWk;
public PartTimeEmployee() {
this(DEFAULT_HOURSPRWK);
}
public PartTimeEmployee(int hoursPerWk) {
this(DEFAULT_HIRE_DATE, DEFAULT_INCOME, hoursPerWk);
}
public PartTimeEmployee(String hireDate, double incomeWk, int hoursPerWk) {
super(hireDate, incomeWk);
this.hoursPerWk = hoursPerWk; // I dont think I need two this.var?
}
public PartTimeEmployee(String firstName, String surname, int age, Address address, String hireDate, double incomeWk, int hoursPerWk) {
super(firstName, surname, age, address, hireDate, incomeWk);
this.hoursPerWk = hoursPerWk;
}
But I feel that the use of two constructors with (super) and this.hoursPerWk = hoursPerWk is wrong, shouldn't '(super)' and 'this.var = var' only need to be written once? If I adjust the code to remove the PTEmp(hiredate, incomewk, hrswk) constructor than I get a 'no constructor found' error in my second constructor. Other edits have led to recursor errors.
So the supplied code works and calls all details in my test class, but is it correctly written (I need to have a Person class that is extended by Employee which is extended by PTEmp or Boss etc.). Appreciate any feedback (or jsut to know this is correct if it is.
Thanks.
Added Employee constructors here...
public class Employee extends Person {
public static final String DEFAULT_HIRE_DATE = "00/00/00";
public static final double DEFAULT_INCOME = 0;
private String hireDate;
private double incomeWk;
public Employee() {
this(DEFAULT_HIRE_DATE, DEFAULT_INCOME);
}
public Employee(String hireDate, double incomeWk) {
this(DEFAULT_FIRSTNAME, DEFAULT_SURNAME, DEFAULT_AGE, new Address(), hireDate, incomeWk);
}
public Employee(String firstName, String surname, int age, Address address, String hireDate, double incomeWk) {
super(firstName, surname, age, address);
this.hireDate = hireDate;
this.incomeWk = incomeWk;
}
Not sure what super() does if you don't specify names, address, etc, but assuming that you can use some defaults, maybe you can call it like this:
public PartTimeEmployee(String hireDate, double incomeWk, int hoursPerWk) {
this(DEFAULT_FIRST_NAME, DEFAULT_SURNAME, DEFAULT_AGE, DEFAULT_ADDRESS, hireDate, incomeWk, hoursPerWk);
}
Consider using builder patterns, if you think that you have too many overloaded constructors:
When would you use the Builder Pattern?
Recently I came into a situation where the builder pattern was very strong, but I had the need to subclass. I looked up some solutions and some suggested generics while others suggested normal subclassing. However, none of the examples I looked at had required fields in order to even begin building an object. I wrote a tiny example to illustrate where I'm getting stuck. At every turn I kept running into a wall of problems where things would return the wrong class, can't override static methods, returning super() returns the wrong data type, etc. I have a feeling there is no way out except excessive use of generics.
What is the correct way to go in this situation?
Tester
import person.Person;
import person.Student;
public class Tester
{
public static void main(String[] args)
{
Person p = Person.builder("Jake", 18).interest("Soccer").build();
// Student s = Student.builder(name, age) <-- It's weird that we still have access to pointless static method
// Student s = Student.builder("Johnny", 24, "Harvard", 3).address("199 Harvard Lane") <-- returns Person builder, not student
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
}
}
Person Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Person
{
// Required
protected String name;
protected int age;
// Optional
protected List<String> interests = new ArrayList<>();
protected String address = "";
protected Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getInterests() { return interests; }
public String getAddress() { return address; }
// person.person does not allow builder construction
// unless all required fields are provided
/* Problem: I have to repeat the constructor fields here, very annoying */
public static Builder builder(String name, int age)
{
Person p = new Person(name, age);
return new Builder(p);
}
public static class Builder
{
Person reference;
protected Builder(Person reference)
{
this.reference = reference;
}
public Builder address(String address)
{
reference.address = address;
return this;
}
public Builder interest(String interest)
{
reference.interests.add(interest);
return this;
}
public Person build()
{
return reference;
}
}
}
Student Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Student extends Person
{
// Required
protected String school;
protected int year;
// Optional
protected List<String> subjects = new ArrayList<>();
// This looks good
public Student(final String name, final int age, final String school, final int year)
{
super(name, age);
this.school = school;
this.year = year;
}
public String getSchool() { return school; }
public int getYear() { return year; }
public List<String> getSubjects() { return subjects; }
/* Here's where my issues are:
* Override doesn't compile on static methods but how else can I describe that I want to
* override this functionality from the Person class?
*
* Extending 'Person' does not enforce that I need to provide 'name', 'age', etc like it would
* if this was a normal design pattern using the 'new' keyword. I have to manually drag fields
* from 'person' and place them here. This would get VERY messy with an additional class
*
* User can STILL call the Person builder on a Student object, which makes no sense. */
/*#Override*/ public static Builder builder(String name, int age, String school, int year)
{
Student s = new Student(name, age, school, year);
return new Builder(s);
}
public static class Builder extends Person.Builder
{
// Student reference; <--- this should not be needed since we already
// have a variable for this purpose from 'Person.Builder'
public Builder(final Student reference)
{
super(reference);
}
/* Things begins to get very messy here */
public Builder subject(String subject)
{
((Student)reference).subjects.add(subject);
// I guess I could replace the reference with a student one, but
// I feel like that infringes on calling super() builder since we do the work twice.
return this;
}
#Override public Student build()
{
// I can either cast here or
// rewrite 'return reference' every time.
// Seems to infringe a bit on subclassing.
return (Student)super.build();
}
}
}
What you write here :
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
is indeed not very natural and you should not need to cast.
We expect rather something like :
Student s = Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build();
Besides all casts you did in the implementation of Student.Builder are also noise and statements that may fail at runtime :
/* Things begins to get very messy here */
public Builder subject(String subject) {
((Student)reference).subjects.add(subject);
return this;
}
#Override public Student build() {
return (Student)super.build();
}
Your main issue is the coupling between the Builder classes and the building methods.
A important thing to consider is that at compile time, the method binding (method selected by the compiler) is performed according to the declared type of the target of the invocation and the declared type of its arguments.
The instantiated type is considered only at runtime as the dynamic binding is applied: invoking the method bounded at compile time on the runtime object.
So this overriding defined in Student.Builder is not enough :
#Override public Student build() {
return (Student)super.build();
}
As you invoke :
Student.builder("Jack", 19, "NYU", 1).address("Dormitory").build();
At compile time, address("Dormitory") returns a variable typed as Person.Builder as the method is defined in Person.Builder :
public Builder address(String address){
reference.address = address;
return this;
}
and it not overriden in Student.Builder.
And at compile time, invoking build() on a variable declared as Person.Builder returns a object with as declared type a Person as the method is declared in Person.Builder as :
public Person build(){
return reference;
}
Of course at runtime, the returned object will be a Student as
Student.builder("Jack", 19, "NYU", 1) creates under the hood a Student and not a Person.
To avoid cast to Student.builder both from the implementation and the client side, favor composition over inheritancy :
public static class Builder {
Person.Builder personBuilder;
private Student reference;
public Builder(final Student reference) {
this.reference = reference;
personBuilder = new Person.Builder(reference);
}
public Builder subject(String subject) {
reference.subjects.add(subject);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder interest(String interest) {
personBuilder.interest(interest);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder address(String address) {
personBuilder.address(address);
return this;
}
public Student build() {
return (Student) personBuilder.build();
}
}
You can now write :
Student s = Student.builder("Jack", 19, "NYU", 1)
.address("Dormitory")
.build();
or even that :
Student s2 = Student.builder("Jack", 19, "NYU", 1)
.interest("Dance")
.address("Dormitory")
.build();
Composition introduces generally more code as inheritancy but it makes the code
both more robust and adaptable.
As a side note, your actual issue is enough close to another question I answered 1 month ago.
The question and its answers may interest you.
A few thoughts as background
Static methods are not so great,
they make unit testing more difficult.
It is fine to put the builder as a static, nested class, but if you are using a builder to construct a class you should make the constructor not-public.
I prefer to have the builder be a separate class in the same package and to make the constructor (of the class that is created by the builder) package access.
Limit the builder constructor parameters.
I'm not a fan of using a class hierarchy for builders.
The Person and Student classes each have a builder.
Some Code
public class PersonBuilder
{
private String address;
private int age;
private final List<String> interestList;
private String name;
public PersonBuilder()
{
interestList = new LinkedList<>();
}
public void addInterest(
final String newValue)
{
// StringUtils is an apache utility.
if (StringUtils.isNotBlank(newValue))
{
interestList.add(newValue);
}
return this;
}
public Person build()
{
// perform validation here.
// check for required values: age and name.
// send all parameters in the constructor. it's not public, so that is fine.
return new Person(address, age, interestList, name);
}
public PersonBuilder setAddress(
final String newValue)
{
address = newValue;
return this;
}
public PersonBuilder setAge(
final int newValue)
{
age = newValue;
return this;
}
public PersonBuilder setInterestList(
final List<String> newValue)
{
interestList.clear();
if (CollectionUtils.isNotEmpty(newValue))
{
interestList.addAll(newValue);
}
return this;
}
public PersonBuilder setName(
final String newValue)
{
name = newValue;
return this;
}
}
public class Person
{
private Person()
{
}
Person(
final String addressValue,
final int ageValue,
final List<String> interestListValue,
final String name)
{
// set stuff.
// handle null for optional parameters.
}
// create gets or the fields, but do not create sets. Only the builder can set values in the class.
}
Well, i was trying to pass arraylist of objects from one activity to another. I have 2 constructors in the class Student.
If, i use, Serializable than the code is like below:
#SuppressWarnings("serial")
public class Student implements Serializable
{
private int studentdID;
private String studentName;
private String studentDept;
public Student(){}
public Student(String name, String dpt)
{ this.studentName = name;
this.studentDept = dpt;}
public Student(int id, String name, String dpt)
{ this.studentdID = id;
this.studentName = name;
this.studentDept = dpt; }
public int getstudentdID() { return studentdID; }
public void setstudentdID(int studentdID) {this.studentdID = studentdID;}
public String getstudentName() { return studentName;}
public void setstudentName(String studentName) {this.studentName = studentName;}
public String getstudentDept() { return studentDept; }
public void setstudentDept(String studentDept) { this.studentDept = studentDept;}
}
But the problem i am facing is that how am i going to do this with parcelable? How am i going to set the values of the variables in class-like i did with Serializable? I mean separately using 2 constructors-one without ID another without the ID?
Did you read how Parcelable works?
You need only one constrcutor for parcelable to read what you pass to it, and Parcelable interface will add a method writeToParcel where you put the data to save.
It's not an automatic process like Serializable, everything is up to you.
The constructor which Parcelable will use will accept only one argument Parcel where you will find some methods like read*(KEY) to read back values.
And in writeToParcel you will write in the Parcel (the argument of the method) the values you want pass to pass with write*(KEY, VALUE).
Parcelable don't care about your constructors or fields.
P.S You will need a CREATOR too. Read some tutorial online to know more about it if you need.
Marco's answer explains why Parcelable doesn't automatically decide what constructor to use - it can't.
However, there is a way around this. Use Parcel.dataAvail(), which
Returns the amount of data remaining to be read from the parcel. That
is, dataSize()-dataPosition().
For example,
public Student(){}
public Student(String name, String dpt)
{
this.studentName = name;
this.studentDept = dpt;}
public Student(int id, String name, String dpt)
{ this.studentdID = id;
this.studentName = name;
this.studentDept = dpt;
}
public Student(Parcel in) {
name = in.readString();
dpt = in.readString();
if(in.dataAvail() > 0) // is there data left to read?
id = in.readInt();
}
^ The above constructor will allow for the necessary variables to be instantiated correctly. Also, you define writeToParcel() something like:
public void writeToParcel(Parcel out) {
out.writeString(name);
out.writeString(dpt);
//0 is the default value of id if you didn't initialize it like
// in the first constructor. If it isn't 0, that means it was initialized.
if(id != 0)
out.writeInt(id);
}
Of course, you'll need to define your CREATOR like so:
public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
public Student createFromParcel(Parcel in) {
return new Student(in);
}
public Student[] newArray(int size) {
return new Student[size];
}
};
#u3l solution is not required..how many constructors are there it doesn't matter.
simple it works go as normal implementation.
I mean no special care is required when multiple constructors present in parcelable.
I am working on a homework assignment. I am confused on how it should be done.
The question is:
Create a class called IDCard that contains a person's name, ID number,
and the name of a file containing the person's photogrpah. Write
accessor and mutator methods for each of these fields. Add the
following two overloaded constructors to the class:
public IDCard() public IDCard(String n, int ID, String filename)
Test your program by creating different ojbects using these two
constructors and printing out their values on the console using the
accessor and mutator methods.
I have re-written this so far:
public class IDCard {
String Name, FileName;
int ID;
public static void main(String[] args) {
}
public IDCard()
{
this.Name = getName();
this.FileName = getFileName();
this.ID = getID();
}
public IDCard(String n, int ID, String filename)
{
}
public String getName()
{
return "Jack Smith";
}
public String getFileName()
{
return "Jack.jpg";
}
public int getID()
{
return 555;
}
}
Let's go over the basics:
"Accessor" and "Mutator" are just fancy names fot a getter and a setter.
A getter, "Accessor", returns a class's variable or its value. A setter, "Mutator", sets a class variable pointer or its value.
So first you need to set up a class with some variables to get/set:
public class IDCard
{
private String mName;
private String mFileName;
private int mID;
}
But oh no! If you instantiate this class the default values for these variables will be meaningless.
B.T.W. "instantiate" is a fancy word for doing:
IDCard test = new IDCard();
So - let's set up a default constructor, this is the method being called when you "instantiate" a class.
public IDCard()
{
mName = "";
mFileName = "";
mID = -1;
}
But what if we do know the values we wanna give our variables? So let's make another constructor, one that takes parameters:
public IDCard(String name, int ID, String filename)
{
mName = name;
mID = ID;
mFileName = filename;
}
Wow - this is nice. But stupid. Because we have no way of accessing (=reading) the values of our variables. So let's add a getter, and while we're at it, add a setter as well:
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
Nice. Now we can access mName. Add the rest of the accessors and mutators and you're now a certified Java newbie.
Good luck.
You need to remove the static from your accessor methods - these methods need to be instance methods and access the instance variables
public class IDCard {
public String name, fileName;
public int id;
public IDCard(final String name, final String fileName, final int id) {
this.name = name;
this.fileName = fileName
this.id = id;
}
public String getName() {
return name;
}
}
You can the create an IDCard and use the accessor like this:
final IDCard card = new IDCard();
card.getName();
Each time you call new a new instance of the IDCard will be created and it will have it's own copies of the 3 variables.
If you use the static keyword then those variables are common across every instance of IDCard.
A couple of things to bear in mind:
don't add useless comments - they add code clutter and nothing else.
conform to naming conventions, use lower case of variable names - name not Name.