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);
Related
I am new at java . I have method witch contain some variables with different data type i.e. String and Array
pNumber=rs.getString("pNumber");
userName=rs.getString("userName");
simOperater=rs.getString("simOperater");
AdharNumber=rs.getString("AdharNumber");
rechargeAmount[i]=rs.getString("rechargeAmount");
activeDate[i]=rs.getString("activeDate");
plainDeatils[i]=rs.getString("plainDeatils");
and I want to return all the variables from single method in java so what approach should I use please help
just return a response object
public class MyResponse {
public String pNumber;
public String userName;
//....
}
usage:
public MyResponse yourMethod() {
MyResponse myResponse = new MyResponse();
myResponse.pNumber=rs.getString("pNumber");
myResponse.userName=rs.getString("userName");
//...
return myResponse;
}
If you don't want to write more lines, you can also set the return type of your method to Object and return your variable as you normally would, but then cast the returned object into the right type as it was before.
e.g.
class test {
static Object test_return(int which) {
String s = "This is a string";
int i = 100;
if(which == 0) {
return s;
} else {
return i;
}
}
public static void main(String args[]) {
String s = (String) test_return(0);
int i = (int) test_return(1);
System.out.println("String: " + s + "\nint: " + i);
}
}
output:
String: This is a string
int: 100
edit:
since you are new to java, you might not understand how types exactly work here. so I would suggest you read this and this to learn more about autoboxing and unboxing
So, I have the following classes:
public class MainClass{
public void run(String infoOne, String infoTwo, String infoThree, String infoFour, String infoFive, String infoSix){
SomeClass someClass = new SomeClass();
someClass.runSomeMethod();
someClass.runSomeMethodTwo( infoOne);
someClass.runSomeMethodThree( infoThree, infoOne, infoSix);
someClass.runSomeMethodFour( infoTwo, infoFive);
someClass.runSomeMethodFive(infoThree, infoFive, infoOne, infoSix);
}
}
public class SomeClass{
public boolean runSomeMethod(){
// do something
}
public boolean runSomeMethodTwo(String arg){
// do something
}
public boolean runSomeMethodThree(String argOne, String argTwo, String argThree){
// do something
}
public boolean runSomeMethodFour(String argOne, String argTwo){
// do something
}
public boolean runSomeMethodFive(String argOne, String argTwo, String argThree, String argFour){
// do something
}
}
As you can see it's a bunch of methods taking only Strings as parameters (but a different amount every time). What I want now is to wrap each single method in a try catch block and log some results. To do that I wanted to put a method in between that handles the logging:
log(SomeClass::runSomeMethodFour);
public void log(????? method, String...args){
try{
if(method.execute(args);
System.out.println("Success!");
} else {
System.out.println("Failed to execute!");
}
} catch (Exception e){
e.printStackTrace();
}
}
Is this possible in some way? To pass a dynamic number of arguments to a lambda function? Or could I do something with generics?
There is no need to create a complicated Reflection-based solution. Your problems stem from the unnecessary attempt to separate the method and the parameter arguments, instead of just encapsulating the entire action like
public class MainClass {
public void run(String infoOne, String infoTwo, String infoThree,
String infoFour, String infoFive, String infoSix) {
SomeClass someClass = new SomeClass();
log(() -> someClass.runSomeMethod());
log(() -> someClass.runSomeMethodTwo(infoOne));
log(() -> someClass.runSomeMethodThree(infoThree, infoOne, infoSix));
log(() -> someClass.runSomeMethodFour(infoTwo, infoFive));
log(() -> someClass.runSomeMethodFive(infoThree, infoFive, infoOne, infoSix));
}
public void log(BooleanSupplier method) {
try {
if(method.getAsBoolean()) {
System.out.println("Success!");
} else {
System.out.println("Failed to execute!");
}
} catch (Exception e ){
e.printStackTrace();
}
}
}
For the work of the log method, only the boolean return value is relevant, which matches the functional signature of BooleanSupplier.
JLS described Method Reference Expression:
The compile-time declaration of a method reference is the method to which the expression refers. In special cases, the compile-time declaration does not actually exist, but is a notional method that represents a class instance creation or an array creation. The choice of compile-time declaration depends on a function type targeted by the expression, just as the compile-time declaration of a method invocation depends on the invocation's arguments.
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
the method reference expression must be assign an exactly Function Interface at compile time. and Function Interface is a SAM Interface. so you can't bind a method handler dynamically by method reference expression at runtime.
but you can using reflection or invoke api to achieve it.
let see each method expression refer to a Function Interface in your SomeClass results in refers to different Function Interface type:
SomeClass it = new SomeClass();
BooleanSupplier first1 = it::runSomeMethod;//bound
Predicate<SomeClass> first2 = SomeClass::runSomeMethod;//unbound
Predicate<String> second1 = it::runSomeMethodTwo;//bound
BiPredicate<SomeClass, String> second2 = SomeClass::runSomeMethodTwo;//unbound
...
Hearing about "reflection" as a comment by Oliver Charlesworth I came up with the following solution:
public class Test {
static Test testLogger = new Test(); //This should be another class ofcourse, but it doesn't matter for this example
public static void main(String[] args) throws NoSuchMethodException, SecurityException{
Test test = new Test();
run(test, "something", "hi", "hai", "blaa");
}
public static void run(Object pageObjectModel, String methodName, String...arguments){
Class<String>[] args = new Class[arguments.length];
Arrays.fill(args, String.class);
try {
testLogger.log(pageObjectModel, pageObjectModel.getClass().getMethod(methodName, args), arguments);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
private void log(Object pageObjectModel, Method method, String...arguments) {
try {
if((Boolean)method.invoke(pageObjectModel, (Object[])arguments)){
System.out.println("Success!");
} else {
System.out.println("Fail!");
}
} catch (Exception e){
e.printStackTrace();
}
}
public boolean something(String one, String two, String three){
System.out.println(one+", "+two+", "+three);
return true;
}
}
This seems to be perfect for what I'm trying to achieve. Although I don't really like reflection due to having bad experiences with it (giving problems with obfuscated code) I think it's fine for this project.
Thanks for helping me in the right direction!
This is the code of the method that I want to simplify. The method name I call of SerializedExpFamMixture class is exactly the value of "model", my question is how to assign the value of "model" directly as the name of the method instead of using "if" to determine which method I should call. Since by using "if", I need to list all the possible values of "model" and judge which method I should use.
Thank you very much for help. I am new to java.
public static SerializedExpFamMixture RateMtxModel(String model)
{
SerializedExpFamMixture result=new SerializedExpFamMixture();
if(model=="kimura1980()")
result=SerializedExpFamMixture.kimura1980();
if(model=="accordance()")
result=SerializedExpFamMixture.accordance();
if(model=="pair()")
result=SerializedExpFamMixture.pair();
return result;
}
One way you can approach this is to use Reflection:
Method method = myClass.getClass().getMethod("doSomething", null);
method.invoke(myClass, null);
Since you are new to Java, it's time for some general pointers:
In Java, we usually name our methods with camelCase, so the first letter is lower case.
Also, in Java we usually leave the opening curly-bracket on the same line as the code (no newline).
Always use final on your variables. At least your parameters. That way you won't overwrite it, and thus won't have to try to figure out which value it actually has at runtime.
Use curly-brackets! Please!
The result variable is not actually needed.
Use the equals-method to compare Strings.
If you only want one result, use else-if
Fixing these things, your method looks like this:
public static SerializedExpFamMixture rateMtxModel(String model) {
if (model.equals("kimura1980()")) {
return SerializedExpFamMixture.kimura1980();
} else if (model.equals("accordance()")) {
return SerializedExpFamMixture.accordance();
} else if(model.equals("pair()")) {
return SerializedExpFamMixture.pair();
}
return new SerializedExpFamMixture();
}
Next, let's look at what you are actually trying to do here. You want to pass some Strings around, and use them as a basis for creating objects. And now, with the advice given here, you will do this using reflection. This does not sound like a very good idea to me. Say you were to go through with this, and this happened:
rateMtxModel("kinura1980");
Small typo, hard to spot, will give unexpected results. If you were actually calling a method the compiler would let you know that you messed up, now you will get no warning (btw did you see both errors in that method call?). The same if someone were to delete the accordance()-method, the compiler would not alert them that this will break the program.
If it was up to be I would just use the static factory-methods in SerializedExpFamMixture directly, but if you have to do it like this (if the task at hand is using a String input to create an object) I would do something like this:
public enum Something {
KIMURA1980("kimura1980()"),
ACCORDANCE("accordance()"),
PAIR("pair()");
private final String stringValue;
private Something(final String stringValue) {
this.stringValue = stringValue;
}
public static Something fromString(final String string) {
for (final Something something : values()) {
if (something.stringValue.equals(string)) {
return something;
}
}
return null;
}
}
public static SerializedExpFamMixture rateMtxModel(final String model) {
if (model == null) {
throw new IllegalArgumentException("model is null!");
}
final Something something = Something.fromString(model);
if (something == null) {
return new SerializedExpFamMixture();
}
switch(something) {
case KIMURA1980:
return SerializedExpFamMixture.kimura1980();
case ACCORDANCE:
return SerializedExpFamMixture.accordance();
case PAIR:
return SerializedExpFamMixture.pair();
default:
return new SerializedExpFamMixture();
}
}
This way, the one place where you will use the Strings is in the enum, the rest of the code will use the enum constants and thus have the safety of the compiler to rely on.
One could also leave the linking between operation and String to the enum, like this:
interface Operation<T> {
public T run();
}
public enum Something {
KIMURA1980("kimura1980()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.kimura1980();
}
}) ,
ACCORDANCE("accordance()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.accordance();
}
}),
PAIR("pair()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.pair();
}
}),
DEFAULT(null, new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return new SerializedExpFamMixture();
}
});
private final String stringValue;
private final Operation<SerializedExpFamMixture> operation;
private Something(final String stringValue, final Operation<SerializedExpFamMixture> operation) {
this.stringValue = stringValue;
this.operation = operation;
}
public static Something fromString(final String string) {
if (string != null) {
for (final Something something : values()) {
if (string.equals(something.stringValue)) {
return something;
}
}
}
return DEFAULT;
}
public SerializedExpFamMixture getCorrespondingSerializedExpFamMixture() {
return operation.run();
}
}
With this setup in the enum (I think the Operation-part can be trimmed out with Java8), the method will be as simple as:
public static SerializedExpFamMixture rateMtxModel(String model) {
return Something.fromString(model).getCorrespondingSerializedExpFamMixture();
}
Use reflection, but you need to consider a few things:
Bug alert! Comparing Strings using == doesn't work as expected in java - use .equals() instead. However, the solution below bypasses that problem
For the general case, which includes methods not visible to the invoker, you need to consider accessibility, both in finding the method and invoking it
You don't need the result variable, and even if using your code, don't need to initialize it
Try this:
String methodName = model.replace("(", "").replace(")", "");
try {
// getMethod() returns only public methods, getDeclaredMethod() returns any visibility
Method method = SerializedExpFamMixture.class.getDeclaredMethod(methodName);
// if the method is not guaranteed to be visible (eg public) you need this:
method.setAccessible(true);
return (SerializedExpFamMixture) method.invoke(null); // how to invoke on the class object
} catch (Exception forBrevity) {
return new SerializedExpFamMixture();
}
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.
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());
}