I met such a Java problem about making instance variables static:
public class Student {
private static String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
and test:
public void testBadStatic() {
Student studentA = new Student("a");
assertEquals("a", studentA.getName());
Student studentB = new Student("b");
assertEquals("b", studentB.getName());
assertEquals("a", studentA.getName());
I think the second assertEquals will fail, since the name variable is static, so it already been assigned "a". However, the correct output should be true for the second assertion and false for the last assertion. Could anybody help me understand this?
Thanks.
I think you are confused between final and static variables.
Final (but not static) variables are tied to instances, and can get values in the constructor, or through static initialization.
Static variables are tied to classes, so they share the same value across one JVM instance for all instances of the given class.
Final static variables offer the most restricted of the two: it is effectivelz a constant: cannot be changed, and is the same for all instances of the given class.
Conclusion: the
this.name = name;
statement updates a value, that is tied to the class, rather than the indvidual instances, so after such a call, each instance will "see" that same value - the result of the last assignment operation.
public Student(String name) {
This is your constructor. Whenever you create an instance of this class, this "method" is called. However, static variables belong to the class. Thus, setting the static String name to a value changes it for all existing instances.
The second assert won't fail, because this line:
Student studentB = new Student("b");
Will change the name variable for all instances.
So when the second assert comes around, Student.name is "b". (Note that this is the Student class I'm referencing here).
The third assert will indeed fail, because the name for all instances (both studentA and studentB are now "b".
In other words:
Student studentA = new Student("a");
// studentA.name = "a"
assertEquals("a", studentA.getName()); // This passes
Student studentB = new Student("b");
// studentB.name = "b"
// studentA.name = "b"
assertEquals("b", studentB.getName()); // This passes
assertEquals("a", studentA.getName()); // This fails
Try :
public class Student {
private static String name;
public Student(String name) {
Studen.name = name;
}
public String getName() {
return Student.name;
}
}
A static variable is not "static" in the sense of "unchanging" - it means that all instances share the same variable, so the second constructor call replaced the value of the static variable with "b".
The second assertion will pass you assign "b" to your static variable in constructor. This is also why the third assertion will fail (expected "a", found "b").
Related
This question already has answers here:
What is meant by immutable?
(17 answers)
Closed 3 years ago.
I was reading about immutable classes, and the ways to make a class immutable were said to be:
1 - Make the class final to prevent inheritance
2 - Make the mutable variables final
3 - Don't provide setter methods.
I think the third condition is unnecessary. When we make a variable final and provide any value to it, after that new value can't be assigned to it even through a setter method (because final variables can't be changed once a value is assigned to it). So why do we need the third condition of not having setter methods?
Am I understanding something in a wrong way?
public class Person{
private String name;
public Person(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Now, it is clear that Person is not an Immutable class. This doesn't mean, an instance of Person can't be a member of another class that is (supposedly) immutable.
public final class MyImmutableClass {
// p is final, so it can't be re-referenced
private final Person p;
public MyImmutableClass(Person p) {
this.p = p;
}
// it can be altered, though
public void setPersonName(String name) {
this.p.setName(name);
}
public String toString() {
return "Person: " + p.getName();
}
}
Now, we have an immutable class, but, it does contain a setter. This setter actively changes a member of the (final) field p.
public static void main(String[] args) {
MyImmutableClass c = new MyImmutableClass(new Person("OriginalName"));
System.out.println(c);
c.setPersonName("AlteredName");
System.out.println(c);
}
And .. there you have it. The member is changed, through the setter (even though the variable was final). Do understand, a 'final variable' is NOT necessarily a constant, in most cases it's state can be changed. The point of a final variable, is that it can not be re-referenced. Note, we could also have a method like this:
public void setPerson(Person p) {
this.p.setName(p.getName());
}
A final variable itself is only a constant in case the type itself is an immutable type, or if it's a primitive, but you should understand that most types out there are mutable.
Is the type immutable or a primitive and it's declared final? Sure, add a setter. But, to what end? Misleading people who use your class?
You are sort of right. A setter, by definition, replaces a field with the given value. If all of the fields are final then you couldn't possibly provide a setter anyway.
My description of how to write an immutable class would be:
Make all fields final
Make sure the type of every field is itself immutable
It's possible to write an immutable class with fields that are mutable if you take great care to ensure that they never change but you need to be very careful in this case.
Yes, it could be reduced to
Make the class final to prevent inheritance;
Make the mutable variables final, and so don't bother providing any setters;
However for education purposes the shorter bullet points probably work better - even if slightly redundant.
The variable mentioned in item 2 can be a reference, which can be mutable (like a list or set) even when the variable itself is final.
This is why we have utilities like Collections.unmodifiableList to make mutable classes virtually immutable.
So the prohibition on setters is to prevent accidentally changing a final variable's state.
Making fields final is not sufficient (or even required) to guarantee immutability. You need to make defensive copies of mutable objects within the immutable class.
class Foo {
private String str;
public Foo(String str) {
this.str = str;
}
public String getString() {
return str;
}
}
The above class is immutable because:
String is immutable.
The field 'str' is private and can't be altered.
Now considering the following MyDate class.
public class MyDate {
private Date; // date is not immutable
public MyDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
The MyDate class above is not immutable because the user can do the following:
Date d = new Date(<someDate>);
MyDate md = new MyDate(d);
d.set(<someDate>); // oops, just changed value in MyDate via external reference.
The same could also be done via getDate().
To make MyClass immutable, make defensive copies of Date in the constructor and getter. These prevent the user of the class from changing the date field either:
Using a reference to the constructor argument.
Upon retrieving the date field via a getter
public class MyDate {
private Date; // date is not immutable
public MyDate(Date date) {
this.date = new Date(date);
}
public Date getDate() {
return new Date(date);
}
}
I am trying the Jacco testing and I am able to test the getStudentId from a class called Student which has:
public String getStudentId() {
return studentId;
}
When I try to test my other class named Product, I get an error - the only difference between the two is in the getX method. The getName method of Product is:
public String getName() {
return this.name;
}
and the error message says:
constructor Product in class Product cannot be applied to given types
The keyword this references the instance of the object you are currently in. Imagine having a class like this:
public class A {
private String property;
public void changeProperty(String property) {
this.property = property
}
}
Outside of the method the variable name property is not ambiguous and references the member variable of class A. But it is ambiguous inside the method changeProperty because there is also the argument named property.
How does Java resolves this conflict? If you just type property you will always reference the object with a smaller scope, so the argument of the method and not the member variable. By using this.property you can reference the member variable again.
If there is no such conflict in your object, like in your example, then you do not need the this statement and this.name is the same as name.
However as prevention of very nasty bugs one could always use this when referencing a member variable, just as good practice. Imagine you would create a method with such a name conflict in the future and forget about the member variable, whoops you easily create a bug that is hard to debug.
Some programmers even go further and do always give member variables other names than arguments, to prevent such name conflicts. For example member variables are often named:
mProperty or
_property
Note that the method this(...) references a constructor of the own object. It can be used in a constructor to pass the task to another constructor like:
public class A {
public A(String fileName) {
this(new File(fileName), true);
}
public A(File file) {
this(file, true);
}
public A(File file, boolean doSomething) {
// Code ...
}
}
Analogously there is also the keyword super which references the parent-class. For example:
public class A {
protected String property;
}
public class B extends A {
private String property;
public void foo() {
// Property of B
System.out.println(property);
// The same
System.out.println(this.property);
// Property of A
System.out.println(super.property);
}
}
This keyword can also be used to reference parent-constructor or other methods of the parent class.
So all in all it is just about resolving such name conflicts.
Now we know that, it is easy to see that the code you posted does not contain the bug.
When you use this.name you are using a attribute defined in your class, the attribute name. However, when you use only name, it could be any variable called so in your code, even the attribute. Example:
public String getName(){
String name = "Mery";
this.name = "Jacob";
return name;
}
This method return the value "Mery". If you put return this.name then you return the value "Jacob".
There's a chance you set studentID to a public variable. Anytime you are using this.whatever to return a variable from a getX function, the this. implies it's a private variable. More likely than not the studentID is public and that's why you got away with no 'this.' in front of it.
If I create an object with a constructor in one class, and the constructor gives the object a property like 'name', is there a way to access specifically the String 'name' from another class? As in, if I have a method that I pass the objects to, and that method needs to access just the String 'name' from the constructor, how do I get to it?
This is probably a bad question that already has an answer, but since I don't know the right terminology to search for it I'm a bit stuck...
You cannot read parameters passed to a constructor from outside the constructor definition, unless that parameter is stored in a field of the class. Of course, if you create an instance of a class like MyClass myObject = new MyClass("Some String");, you can access some string in the scope of the code that created the object.
The ways that a field of a class, say the field fieldName from an instance myObject an a class MyClass can be accessed by another class are:
If the field is public, access by myObject.fieldName
If the field is protected, access it by subclassing MyClass
If MyClass has a getter for the field: myObject.getFieldName()
If the field is private and does not have a getFieldName() method, then it cannot be accessed from outside of the class.
Here are a couple of classes that I think demonstrate what you are wanting to do.
I have a Person class that has a name field and a Friend class that has a method called sayHello and it accesses the name property of Person.
public class Main
{
private static class Friend
{
public void sayHello(Person person)
{
System.out.println("Hello " + person.getName());
}
}
private static class Person
{
private String name;
public Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
public static void main(String[] args)
{
Person person = new Person("John Smith");
Friend friend = new Friend();
friend.sayHello(person);
}
}
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.
I have a scenario where some of the final variables declared at class level are optional in some cases.
That means I have different constructors. Some of those have to leave the member variables with null values.
Since I have declared the variable as final, I am forced to initialize those in the constructor. So I have to add var = null; statement in the constructor.
But explicitly assigning variables to null is considered to be a bad practice and tools like PMD, reports it as a violation. (Controversial rule in PMD. But do not want to switch it off, since I do not want the null assignment to be practiced in other areas of my code)
Any other suggessions or good practices to achieve this?
You can use constructor chaining, passing null to the values that are not used in your instance. (You can either use the super constructor if we are discussing inheritance, or a different constructor in the same class.)
After all I would reconsider the design of your classes, for example extract the optional part to a different class.
Instance variables are assigned default values(null in case of custom object).
So unless you are really assigning some value to your final variables why bother about null assignments?
As user has pointed out in the comment above does not stand good for final variables. So what can be done define a no-arg constructor with all final values set to null.
Now in individual arg constructors place a call to this default constructor by using this() as the 1st statement .. then you can assign values depending on the arguments passed.
What you are looking for is the builder pattern.
Say you have a class with a constructor that accepts all the values:
class Job {
private final Integer id;
private final String name;
private final Boolean retry;
public class Job(Integer id, String name, Boolean retry) {
this.id = id;
this.name = name;
this.retry = retry;
}
}
Now, you want to let other create different flavors of that object while keeping it immutable, removing the default value logic from it and keeping it clean. You create a new builder class:
class JobBuilder {
// Values the user MUST provide are non-initialized and
// declared as final
private final Integer id;
// Values the user MAY provide are initialized with default values
// and are not final
private String name = "[none]";
private Boolean retry = true;
public class JobBuilder(Integer id) {
this.id = id;
}
public JobBuilder name(String name) {
this.name = name;
}
public JobBuilder retry(Boolean retry) {
this.retry = retry;
}
public Job build() {
return new Job(this.id, this.name, this.retry);
}
}
Now, you can create different job objects easily while enforcing the requirements:
Job job1 = JobBuilder(1).name("firstJob").retry(false).build();
Job job2 = JobBuilder(2).name("secondJob").build();
Job job3 = JobBuilder(3).build();