This is a much simplified version of a class file I'm working on, I've just used these classes to show my problem.
public class Test {
private String string1 = null;
private String string2 = null;
private void setString(String s) {
s = "hello";
}
private void set() {
setString(string1);
setString(string2);
}
public void print() {
System.out.println(string1);
System.out.println(string2);
}
public void initialise() {
set();
print();
}
}
public class StringTest {
public static void main(String[] args) {
Test test = new Test();
test.initialise();
}
}
Anyway, basically, after this the two string variables are still null. What causes this problem? and how can I get around it? (Still learning the fundamentals of java - if that's relevant).
Any help/guidance is greatly appreciated.
Because a String is immutable, any change you make inside the method will be local to that method.
private void setString(String s) {
s = "hello";
}
s can't be changed.
I can think of two options.
Option 1
Make s an instance variable (field) and change your method.
public class Test {
private String string1 = null;
private String string2 = null;
private String s;
private void setString(String s) {
this.s = "hello";
}
Option 2
Pass in a StringBuilder/StringBuffer instead of String.
Your problem is that Java is pass by value, so your setString method does not do anything. To change the value of string1, you need to have string1 = xxxx; somewhere in your code. For example:
private void set() {
string1 = getDefaultString();
string2 = getDefaultString();
}
private String getDefaultString() {
return "hello";
}
You are setting the value of "s" not string1 or string2
try something like.
private void setString(String s) {
string1 = "hello";
string2 = "world";
}
By making this assignation
s = "hello";
weare creating a new object. It will have a different memory address than the one passed with setString(string1);
We would need to change the object received at
private void setString(StringBuilder s) { ... }
instead of creating a new one. We need to use exactly the same object we were passed in this method. Can't create a new one, as the caller method wouldn't notice (parameters in Java are never output parameters).
As Java's String is inmutable, we can't change its content: just create a new String object.
As #adarshr pointed out, we can use StringBuilder or StringBuffer, which are mutable, and change their content. But we can't just replace String with StringBuilderin you code. We have to make sure that:
The StringBuilder is initialized before being passed to setString().
The StringBuilder is not created again inside setString() (we are using the same one we were passed).
So, replacing this in your code should work:
private StringBuilder string1 = new StringBuilder();
private StringBuilder string2 = new StringBuilder();
private void setString(StringBuilder s) {
s.append("hello");
}
you misunderstand of java parameter passing, try public void setString1(String s) { string1 = s; }
What are you trying to initialize the values of string1 and string2 to? The two member variables start out being null.
If I were to treat Test class as a POJO (plain old Java object), I would add getter/setter methods as follows:
public class Test {
private String string1;
private String string2;
public String getString1() {
return this.string1;
}
public String getString2() {
return this.string2;
}
public void setString1(String s) {
this.string1 = s;
}
public void setString2(String s) {
this.string2 = s;
}
/*
* Additional methods
*/
public void set() {
setString1("hello");
setString2("world");
}
public void print() {
System.out.println(this.string1);
System.out.println(this.string2);
}
public void initialize() {
set();
print();
}
}
ur application is assigning the hello word in parameter s..but the variable s is a parameter type thats y the reflection of s will be in setString() method block...not anywhere else...use instance variable or static variable to see the reflection of ur assignment.
Related
I have an enum:
public enum ListEnums {
TEST("test1"),
TEST2("test2");
private final String txt;
ListEnums(String str){
txt = str;
}
#Override
public String toString(){return txt;}
I want get the enum string without call .toString().
Like:mymethod(ListEnums.TEST);
No: mymethod(ListEnums.TEST.toString());
Is it possible?
EDIT
The string return must be contains special chars.
here:
public enum ListEnums {
TEST("test1"),
TEST2("test2);
private final String txt;
ListEnums(String str){
txt = str;
}
#Override
public String toString(){
return txt;
}
if you call ListEnum.TEST.name() you will get TEST which is almost the same as calling toString()... if you instead do ListEnum.TEST then the name will be printed...
so Renaming the Enum constants is the way to go...
and the best part is: you will get rid off the constructor, the toString method and the variable txt...
you just dont need it anymore. :)
It's OK, and a good practice to use getters in your enums. Also good to keep your constructor private (though enums are private by default)....
public enum ListEnums {
TEST("test1"),
TEST2("test2");
private final String txt;
private ListEnums(String str){
txt = str;
}
public String getTxt() {
return txt;
}
}
public static void main(String[] args) {
System.out.println(ListEnums.TEST.getTxt());
}
It may be a common question but I couldn't find nice explanation for it. I am trying to understand the encapsulation of reference variables in Java.In the below code:
class Special {
private StringBuilder s = new StringBuilder("bob");
StringBuilder getName() { return s; }
void printName() { System.out.println(s); }
}
public class TestSpecial {
public static void main (String[] args ) {
Special sp = new Special();
StringBuilder s2 = sp.getName();
s2.append("fred");
sp.printName();
}
}
Output: bobfred
At first I thought making our field private and providing a getter method, it's a good encapsulation technique. But when I took closer look on it, I saw that when I invoke getName(), I do in fact return a copy, just like Java always does. But, I am not returning a copy of the StringBuilder object. I am returning a copy of the reference variable that point to the one and only StringBuilder object. So, at the point that getName() returns, I have one StringBuilder object and two reference variables pointing to it ( s and s2).
What are the techniques to make it well encapsulated?? A good explanation with code example expected :) . Thanks in advance.
There are two basic approaches I can think of.
The first is to only return immutable values. The caller can then do what he wants without risking the integrity of your object. In your case, the immutable type would be String:
class Special {
private String s = "bob";
String getName() { return s; }
void printName() { System.out.println(s); }
}
public class TestSpecial {
public static void main (String[] args ) {
Special sp = new Special();
String s2 = sp.getName();
s2 += "fred";
// Alternatively: StringBuilder s2 = new StringBuilder(sp.getName()).append("fred");
sp.printName(); // prints "bob"
}
}
Note: If s needs to be a StringBuilder, you can return s.toString().
The other option is to return a mutable value, but create a defensive copy in your getter. In other words, return a separate reference with duplicate data:
class Special {
private StringBuilder s = new StringBuilder("bob");
StringBuilder getName() { return new StringBuilder(s); } // return a copy of s
void printName() { System.out.println(s); }
}
public class TestSpecial {
public static void main (String[] args ) {
Special sp = new Special();
StringBuilder s2 = sp.getName();
s2.append("fred");
sp.printName(); // prints "bob"
}
}
There can be multiple ways to apply encapsulation to mutable object.
By providing copy constructor (in above example new StringBuilder(oldBuilder.toString())
public class Student{
private String name;
public Student(Student s){
this.name = s.name;
}
}
Using prototype pattern with clone method. But copy Constructor is recommend over clone method.
public Student implements Cloneable{
private int rollNo;
private String name;
public Student clone(){
Student s = (Student)super.clone();
s.name = this.name;
s.rollNo = this.rollNo;
return s;
}
}
public class Clazz{
private Map students= new HashMap();
public student getStudent(int rollNo){
Student s = students.get(rollNo);
return s.clone();
}
}
Using immutable form of mutable object. e.g. Collections.unmodifiablecollection().
Whenever we return collection or array, always return read-only form. So modifications to collection will not impact the state of the object.
Ok so this is what I have
public class Register {
public String propertyID;
public String PPSNumber;
Register(String aPropertyID, String aPPSNumber) {
propertyID = aPropertyID;
PPSNumber = aPPSNumber;
}
public void setPPSNumber(String aPPSNumber) {
PPSNumber = aPPSNumber;
}
public String getPPSNumber() {
return PPSNumber;
}
public String getPropertyID() {
return propertyID;
}
}
Then I have this
public static ArrayList<Register> registers = new ArrayList<Register>();
public static void main(String[] args) {
String userInput1 = "", userInput2 = "", userInput3 = "";
userInput1 = JOptionPane.showInputDialog("Enter your PPSNumber");
userInput2 = JOptionPane.showInputDialog("Enter your propID");
registers.add("number", "id");
}
I don't understand why It wont let me add to the ArrayList. Is there some way of adding class types to ArrayLists?
Try this instead :
registers.add(new Register("number","id"));
EDIT 1:
To answer your question, you can create a separate "register" and the use the getters :
Register aRegister = new Register("number","id");
registers.add(aRegister);
System.out.println(aRegister.getPropertyID()+" "+ aRegister.getPPSNumber());
Your List is of type Register so you need to add object of Register class only.
Nothing wrong in create as many Register objects as required.
You can implement toString() method inside Register class then the below sysout will work given the register variable is initialized with Register object. Check this How to override toString() properly in Java? to know about toString implementation.
System.out.println(register)
I found the following question Is Java "pass-by-reference" or "pass-by-value"?.
I read almost all of it, but could not find out yet what should I do if I want the foo(-) method, to change my String's value? (maybe or not reference too, it doesn't matter to me).
void foo(String errorText){
errorText="bla bla";
}
int main(){
String error="initial";
foo(error);
System.out.println(error);
}
I want to see bla bla on the console. Is it possible?
You can't change the value of errorText in foo as the method is currently declared. Even though you are passing a reference of the String errorText into foo, Java Strings are immutable--you can't change them.
However, you could use a StringBuffer (or StringBuilder). These classes can be edited in your foo method.
public class Test {
public static void foo(StringBuilder errorText){
errorText.delete(0, errorText.length());
errorText.append("bla bla");
}
public static void main(String[] args) {
StringBuilder error=new StringBuilder("initial");
foo(error);
System.out.println(error);
}
}
Other solutions are to use a wrapper class (create a class to hold your String reference, and change the reference in foo), or just return the string.
Either use the return value of the method or create a wrapper class.
Have it return the value:
String foo(String errorText){
return "bla bla";
}
int main(){
String error="initial";
error = foo(error);
System.out.println(error);
}
Wrap the value in an object:
class StringWrapper {
private String string;
public StringWrapper(String s) {
this.string = s;
}
public String getString() {
return this.string;
}
public void setString(String s) {
this.string = s;
}
}
void foo(StringWrapper errorText){
errorText.setString("bla bla");
}
int main(){
StringWrapper error=new StringWrapper("initial");
foo(error);
System.out.println(error.getString());
}
Yes you can change this with help of reflections but its against rule.
void foo(String errorText) {
try {
final Class<String> type = String.class;
final java.lang.reflect.Field valueField = type.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(errorText, "bla bla".toCharArray());
} catch (Exception e) {
}
}
public static void main(String[] args) {
String error = new String("initial");
foo(error);
System.out.println(error);
}
String values are immutable -- so once you get a value, you're stuck with it.
Literal Strings are treated specially by the Java language; your code is roughly equivalent to:
void foo(String errorText){ // at this point, errorText refers to the original string
errorText=new String("bla bla"); // now it refers to a new string
}
int main(){
String error=new String("initial"); // error is a reference to the original string
foo(error); // pass a *copy* of the reference
System.out.println(error);
}
In other words, you're just pointing the local reference errorText at a different String object, which affects nothing outside the method.
More generally, though, Strings are immutable; there's no way to modify them.
You can reassign the String reference:
String foo(String err) {
return "bla blah"
}
error = foo(error);
I have this very awkward question...
void changeString(String str){
str = "Hello world":
}
main(){
String myStr = new String("");
changeString(myStr);
}
When main returns, the value is still "" and not "Hello world". Why is that?
Also, how do I make it work? Let's say I want my function changeString to change the string it got to "Hello world".
Everyone explained why it doesn't work, but nobody explained how to make it work. Your easiest option is to use:
String changeString() {
return "Hello world";
}
main() {
String myStr = new String("");
myStr = changeString();
}
Although the method name is a misnomer here. If you were to use your original idea, you'd need something like:
void changeString(ChangeableString str) {
str.changeTo("Hello world");
}
main() {
ChangeableString myStr = new ChangeableString("");
changeString(myStr);
}
Your ChangeableString class could be something like this:
class ChangeableString {
String str;
public ChangeableString(String str) {
this.str = str;
}
public void changeTo(String newStr) {
str = newStr;
}
public String toString() {
return str;
}
}
A quick lesson on references:
In Java method everything is passed by value. This includes references. This can be illustrated by these two different methods:
void doNothing(Thing obj) {
obj = new Something();
}
void doSomething(Thing obj) {
obj.changeMe();
}
If you call doNothing(obj) from main() (or anywhere for that matter), obj won't be changed in the callee because doNothing creates a new Thing and assigns that new reference to obj in the scope of the method.
On the other hand, in doSomething you are calling obj.changeMe(), and that dereferences obj - which was passed by value - and changes it.
Java uses a call by value startegy for evaluating calls.
That is, the value is copied to str, so if you assign to str that doesn't change the original value.
If the changing of your String happens very often you could also assign a StringBuffer or StringBuilder to your variable and change its contents and only convert it to a String when this is needed.
Expanding a bit on NullUserException's excellent answer, here's a more general solution:
public class Changeable<T> {
T value;
public Changeable(T value) {
this.value = value;
}
public String toString() {
return value.toString();
}
public boolean equals(Object other) {
if (other instanceof Changeable) {
return value.equals(((Changeable)other).value);
} else {
return value.equals(other);
}
}
public int hashCode() {
return value.hashCode();
}
}
Yura's original code can then be rewritten as:
void changeString(Changeable<String> str){
str.value = "Hello world":
}
void main() {
Changeable<String> myStr = new Changeable<String>("");
changeString(myStr);
}
And, just for fun, here it is in Scala:
class Changeable[T](var self: T) extends Proxy;
object Application {
def changeString(str: Changeable[String]): Unit = {
str.self = "Hello world";
}
def main(): Unit = {
val myStr = new Changeable("");
changeString(myStr);
}
}
Because the reference myStr is passed by value to the function changeString and the change is not reflected back to the calling function.
P.S : I am not a Java guy.
Bill, I have a solution to your problem which uses a List as a pointer in java!
void changeString(List<String> strPointer ){
String str = "Hello world";
strPointer.add(0, str);
}
main(){
LinkedList<String> list = new LinkedList<String>();
String myStr = new String("");
changeString(list);
myStr = list.get(0);
System.out.println( myStr );
}
This answer takes a little extra work to insert and get out the string from the list, however the final line will print "Hello world!"
I hope this can help others as well!
-Port Forward Podcast
Here's the one more solution by StringBuffer/StringBuilder worked for me.
static void changeValue(StringBuilder str){
str.append("newValue");
}
main(){
StringBuilder originalVal= new StringBuilder();
changeValue(originalVal);
System.out.println(originalVal.toString());
}