I am learning about immutable Objects. I am trying this code
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I want to restrict the calling class from changing the value of name variable of NormalObject
But the following code changes the value
ImmutableObject obj = new ImmutableObject("Siddle");
System.out.println(obj.getObj().getName()); //prints Siddle
obj.getObj().setName("Kelly");
System.out.println(obj.getObj().getName()); //prints Kelly
How to restrict it?
For an object to be immutable, all of its properties must be immutable. Its state must not be changeable.
To do that, you have to put an immutable facade on NormalObject, you can't directly return a NormalObject. The method that returns it will also need a different return type, you can't return NormalObject but actually return something that doesn't behave like a NormalObject.
E.g.:
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
private final ImmutableNormalObject objFacade = new ImmutableNormalObject(obj);
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public ImmutableNormalObject getObj() {
return objFacade;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ImmutableNormalObject {
private NormalObject obj;
public ImmutableNormalObject(Normalobject o) {
this.obj = o;
}
public String getName() {
return obj.getName();
}
}
Alternately, if it's acceptable to copy the object and it has a copy constructor (or you can add one), you could do that, but copy-and-return is expensive.
You can do this by returning a copy of your normalObject in getter:
public NormalObject getObj() {
return new NormalObject(obj.getName());
// or you can make a copy constructor:
// return new NormalObject(obj);
}
Or you can make a wrapper for your NormalObject that ignores name setter, but it brakes logic.
Please change Your NormalObject code to
public final class ImmutableObject {
private final String name;
// initialise it to null
private final NormalObject obj = null;
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
// use the Constructor for setting name only once during initialization of ImmutableObject via its constructor
obj = new NormalObject(name);
//obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
NormalObject Class
public class NormalObject {
private String name;
public NormalObject(name){
this.name = name;
}
public String getName() {
return name;
}
//Remove any setter on NormalObject
/*public void setName(String name) {
this.name = name;
}*/
}
In an immutable object, if a User tries to change the state of the Object. either you won't allow or return a new Instance of the Immutable class.
So, since Date is a mutable class.
You can create an immutable wrapper around date, and you can expose only those methods, that are subject to be used in your Immutable-Date's perspective, but you return a new instance of your Immutable class, with the changed attribute of your new Date.
I don't think final would be required for Immutable variable, because it is already private and Immutable.
Example :
public class Main{
private ImmutableDate immutableDate;
public Main() {
this.immutableDate = new ImmutableDate(new Date());
}
public Main(Date date){
this.immutableDate = new ImmutableDate(date);
}
public ImmutableDate getDate() {
return immutableDate;
}
public class ImmutableDate{
// private constructor, so this can only be instantiated within the outer class
// therefore, final keyword not required for Date, as no access given to the variable
private Date date;
private ImmutableDate(Date date) {
this.date = date;
}
// Example methods from Date, that are required for our Immutable implementation
public Main setTime(long time){
Date date1 = new Date();
date1.setTime(time);
return new Main(date1);
}
#Override
public String toString() {
return date.toString();
}
}
}
Related
I am trying to serialise an object by converting it first to another object. At the same time I do not want to serialise twice the same object so I am using #JsonIdentityReference to serialise it only the first time. However since the converter will always create a new output object for the same input object the entire object is serialised every time. Is there a way to avoid serialising twice the same object while at the same time using a converter?
The Domain class
#JsonSerialize(converter = BooleanWithNameConverter.class)
#JsonDeserialize(converter = BooleanWithNameDtoConverter.class)
public class BooleanWithName {
private final boolean value;
private final String name;
BooleanWithName(String name, boolean value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public boolean getValue() {
return value;
}
}
The DTO
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class BooleanWithNameDto {
private final boolean value;
private final String name;
#JsonCreator
BooleanWithNameDto(#JsonProperty("name") String name, #JsonProperty("value") boolean value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public boolean getValue() {
return value;
}
}
The converter to the DTO
public class BooleanWithNameConverter extends StdConverter<BooleanWithName, BooleanWithNameDto> {
#Override
public BooleanWithNameDto convert(BooleanWithName obj) {
return new BooleanWithNameDto(obj.getName(), obj.getValue());
}
}
The converter back to the domain object
This is not strictly needed since the problem can be seen just by examining the json string but helpful in order to add a small unit test demonstrating the problem
public class BooleanWithNameDtoConverter extends StdConverter<BooleanWithNameDto, BooleanWithName> {
#Override
public BooleanWithName convert(BooleanWithNameDto obj) {
return new BooleanWithName(obj.getName(), obj.getValue());
}
}
A small unit test that fails
public class UnitTest {
#Test
public void testConverter() throws JsonProcessingException {
BooleanWithName booleanWithName = new BooleanWithName("dummy", true);
ObjectMapper objectMapper = new ObjectMapper();
BooleanWithName[] value = { booleanWithName, booleanWithName };
BooleanWithName[] readValue = objectMapper.readValue(objectMapper.writeValueAsString(value), BooleanWithName[].class);
Assertions.assertEquals(readValue[0], readValue[1]);
}
}
Thanks a lot!
I am trying to pull a field via reflection that is an array of the below class.
package com.api.Person
public class Person {
private String name;
private int age;
public Person() {
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.client.Client
public class Client{
...
People[] peoples;
...
}
I know how to get the field I am looking for and declare my methods. So below, obj would be my People[] and methodgetAge and methodsetAge are two methods I have defined that act on a Person class. How do I take my obj and loop through it to get individual People and call methodgetAge on each person?
Class<?> mainClass = cl.loadClass("com.client.Client");
Class<?> peopleClass = cl.loadClass("com.api.Person");
Field allPersons = mainClass.getDeclaredField("peoples");
allPersons.setAccessible(true);
Object obj = allPersons.get(mainClass);
Method methodgetAge = peopleClass .getDeclaredMethod("getAge");
Method methodsetAge = peopleClass .getDeclaredMethod("setAge", int.class);
I have Fragments call CategoryFragments where I am adding a list name catList for that I define the variable in Class name CategoryMODEL and variable are
public class CategoryModel {
private String docID;
private String name;
private int noOfTests;
public CategoryModel() {
this.docID = docID;
this.name = name;
this.noOfTests = noOfTests;
}
public String getDocID() {
return docID;
}
public void setDocID(String docID) {
this.docID = docID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNoOfTests() {
return noOfTests;
}
public void setNoOfTests(int noOfTests) {
this.noOfTests = noOfTests;
}
}
but when I try to use add item in my catLIst I get error that Change Signature of CategoryModel
Because you have an empty constructor but inside your are init the values with themself, so create a constructor with params:
public CategoryModel(String docId, String name, int noOfTests) {
// Init your scope variables
}
I have an past exam question that says:
"Create a class Element that records the name of the element as a String and has a public method, toString that returns the String name. Define a constructor for the class (that should receive a String to initialise the name)."
I gave it a go and don't where to go from here...
main class is:
public class builder {
public static void main(String[] args) {
element builderObject = new element(elementName);
}
}
and constructor is:
import java.util.*;
class element {
public int getInt(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the first number");
String elementName = scan.nextLine();
System.out.println("%s");
}
public String toString() {
return elementName;
}
}
Don't get frustrated. Please read java tutorials first and understand the concepts. your exam question is very clear on what you need to do. Atleast for this question, you need to know what is constructor, the purpose of having toString() in a class.
May be the below can help you.
public class Element {
private String elementName;
public Element(String elementName) {
this.elementName = elementName;
}
#Override
public String toString() {
return elementName;
}
}
I can't think of a way to explain this without actually giving the answer, so....
public class Element { /// Create class Element
private final String name; // Record the 'name'
public Element(String name) { // constructor receives and sets the name
this.name = name;
}
public String toString() { // public method toString() returns the name
return name;
}
}
You are missing the constructor itself. The point of constructors is to initialize the object, usually by saving the given parameters to data members.
E.g.:
class Element {
/** A data member to save the Element's name */
private String elementName;
/** A constructor from an Element's name*/
public Element(String elementName) {
this.elementName = elementName;
}
#Override
public String toString() {
return elementName;
}
}
class Element {
private String name = "";
/**
/* Constructor
/**/
public void Element(final String name){
this.name = name;
}
#Override
public String toString(){
return name;
}
}
You don't have a constructor in there. A constructor typically looks something like this:
public class MyClass {
private String name;
private int age;
//This here is the constructor:
public MyClass(String name, int age) {
this.name = name;
this.age = age;
}
//here's a toString method just for demonstration
#Override
public String toString() {
return "Hello, my name is " + name + " and I am " + age + " years old!";
}
}
You should be able to use that as a guideline for making your own constructor.
class Element
{
private String name = "UNSET";
public String getName() { return name; }
public Element(String name) {
this.name = name;
}
public String toString() {
return getName();
}
}
You are missing a constructor you might be looking for something like this
public class Element{
private String name;
public Element(String name){ //Constructor is a method, having same name as class
this.name = name;
}
public String toString(){
return name;
}
}
A note
I take you are starting with java, In java class names usually start with capital letter, thus element should be Element. Its important that one picks up good habits early..
How to assign value to this function in Java incompatible types?
public class CustomerInfo implements Serializable {
private static final long serialVersionUID = 9083257536541L;
protected String id;
protected String searchkey;
protected String taxid;
protected String name;
protected String postal;
/** Creates a new instance of UserInfoBasic */
public CustomerInfo(String id) {
this.id = id;
this.searchkey = null;
this.taxid = null;
this.name = null;
this.postal = null;
}
public String getId() {
return id;
}
public String getTaxid() {
return taxid;
}
public void setTaxid(String taxid) {
this.taxid = taxid;
}
public String getSearchkey() {
return searchkey;
}
public void setSearchkey(String searchkey) {
this.searchkey = searchkey;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPostal() {
return postal;
}
public void setPostal(String postal) {
this.postal = postal;
}
public String printTaxid() {
return StringUtils.encodeXML(taxid);
}
public String printName() {
return StringUtils.encodeXML(name);
}
#Override
public String toString() {
return getName();
}
}
private CustomerInfo selectedCustomer;
public CustomerInfo getSelectedCustomer() {
// construct a CustomerInfo from the data in your String
return selectedCustomer;
}
private void jcmdOKActionPerformed(java.awt.event.ActionEvent evt) {
selectedCustomer = (CustomerInfo) jListCustomers.getSelectedValue();
//test code
String testing = m_jtxtName.getText();
System.out.println("Now the selectedCustomer is dispayed!");
System.out.println(selectedCustomer);
System.out.println(testing);
//test code
dispose();
}
In the above shown code, I need the string testing value to be assigned to selectedCustomer. How can I assign the value? This is the error I get:
selectedCustomer = m_jtxtName.getText();
incompatible types
required: CustomerInfo
found: String
You can't!!!
selectedCustomer is an object of type CustomerInfo.
m_jtxtName.getText() returns a String
You can't assign a String to a CustomerInfo.
Probably you need to do something like:
int id = 1; //Or whatever new id you have.
String name = m_jtxtName.getText();
selectedCustomer = new CustomerInfo(name); //or whatever id you have.
selectedCustomer.setName(name); //or whatever name you have.
EDIT:
Something is missing from your class. Either it needs setter methods (it has only getters now, so you can't set other properties as name etc) or it needs a constructor with four arguments like:
public CustomerInfo(String id, String searchKey, String taxid, String name, String postal) {
this.id = id;
this.searchKey = searchKey;
// etc
In this case, you might have six jtextfields in your screen, so te user can fill all fields and the create the Customerinfo object by passing all parameters to the constructor.
you cannot do it by simply casting a String to a CustomerInfo object, but you could extend your CustomerInfo but you could try something like this:
public class CustomerInfo {
[...]
public static CustomerInfo createCustomerInfo(String data) {
// construct a CustomerInfo from the data in your String
return createdCustomerInfo;
}
}
I don't know what data you have in that String so i can not give you an advice how to implement this. e.g. If it is the ID you could use this to retrieve the CustomerInfo from database or something like that.