Dynamic if statement evaluation problem with string comparison - java

I tried the example given in this thread
to create if statement dynamically using BeanShell. But it is not working fine. Instead of using "age" variable as integer, i have used string in the below example. I am getting "fail" as answer instead of "success".
Can anyone help me?
/*
To change this template, choose Tools | Templates
and open the template in the editor.
*/
import java.lang.reflect.*;
import bsh.Interpreter;
public class Main {
public static String d;
public static void main(String args[])
{
try {
String age = "30";
String cond = "age==30";
Interpreter i = new Interpreter();
i.set("age", age);
System.out.println(" sss" + i.get("age"));
if((Boolean)i.eval(cond)) {
System.out.println("success");
} else {
System.out.println("fail");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
Thanks,
Mani

You have to choose either numeric comparison or String comparison. This requires using a compatible condition and type for age.
Numeric:
int age = 30;
String cond = "age==30";
String:
String age = "30";
String cond = "age.equals(\"30\")";

When you compare two objects with the == operator, you're comparing two references. You're essentially asking whether two different names refer to the same object in memory.
To compare the actual values of objects, you need to use the equals() method. This is something that's very important to understand about Java.

#Matthew Flaschen is correct. As an aside, you can simplify your output as follows:
System.out.println(cond + " is " + i.eval(cond));
which produces
age == 30 is true

You are using == to compare string types. Try using age.equals("30") instead.
EDIT: to show it working
If you use this as the definition of cond:
String cond = "age.equals(\"30\")";
Output:
sss30
success
In response to the question about using =="30" instead, here is the answer to that:
If your String age is interned, because it is a compile-time constant for example, then it could be true.
final String age = "30";
However if you explicitly new the String or it is otherwise not interned, then it will be false.
String age = new String("30");
You can run both examples to see this in effect. Possibly - you may get fail for both.
Now, just because interning exists doesn't mean one should ever rely on it for comparing String types. The == operator should only be used to compare primitives to each other, and to compare reference types to see if they point to the same object, so for reference types we could say it is seeing if two objects are identical instead of equal.
Sometimes through the magic of the JVM and JDK, String and other primitive wrappers like Integer may be comparable with ==, but the situations for this are limited and not reliable.

The string compare with "==" in the bsh interpreter is not working like expected.
It is working like that: (copied from the link bellow)
Beanshell handles '==' like java and not e.g. like JavaScript. That means you've got to use "equals" to compare instances.
bsh % "abc" == "abc"; => true
bsh % "abc" == new String("abc"); => false
bsh % "abc".equals("abc"); => true
bsh % "abc".equals(new String("abc")); => true
further information here: https://code.google.com/p/beanshell2/issues/detail?id=86
So you have to use the ".equal()", or compile your own bsh version, like i did it.
(read the complete issue above)

Related

Why the String returned by String.toLowerCase() is not interned?

I was asked to predict the output of this code :
String party1 = "party";
String party2= "PARTY".toLowerCase();
if(party1==party2){
System.out.println("EQUAL");
}else{
System.out.println("NOT EQUAL");
}
I thought the result of toLowerCase() would be interned automatically and hence the same reference to party1 would be returned, but I was wrong since the code ouptuts "NOT EQUAL".
Does toLowerCase() (toUpperCase() as well) return a new String ?
Edit : I am not using == to compare Strings, I just want to know about the behaviour of toLowerCase() method
If the result of any String operation were internalised, the pool would be growing extremely and unnecessarily rapidly. Any intermediate action on a Spring would result in a new element in the pool. It would be beneficial for a VERY small number of cases within a VERY specific domain. Therefore, it's not the default policy and that's why intern() exists.
String party1 = "party";
String party2 = "PARTY".toLowerCase().intern();
System.out.println(party1 == party2 ? "EQUAL" : "NOT EQUAL");
There is nothing special about toLowerCase(): any String method would return a new String since Strings are immutable.
Answer is in String.toLowerCase() method itself.. as you can check by your self toLowerCase() method returns a new lowercase string (a new String object) and == checks for the object reference and returns true if they are same which are different in your case.. that's why its returning false.. hope you get it.
public String toLowerCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
........
............
return new String(result, 0, len + resultOffset);
}
Strings are immutable.Generally whenever we try to modify a string a new string literal is created in the Constant literal pool and a new reference to it is returned.Although "party" and "PARTY".toLowerCase() have same values i.e. party but in constant pool they refer to two different literals and have two different references. By using == operator you are comparing references and not the values.Since they have different references it prints NOT Equal.Try using party1.equals(party2) and it will print EQUAL as it compares content and not reference.
As of my knowledge on any operations on string it will duplicate the string so never use '==' instead use .equals()
String party1 = "party";
String party2= "PARTY".toLowerCase();
System.out.println(party1);
System.out.println(party2);
if(party1.equals(party2)){
System.out.println("EQUAL");
}else{
System.out.println("NOT EQUAL");
}
This gave the result equal

How does this Java code snippet work? (String pool and reflection) [duplicate]

This question already has answers here:
Is a Java string really immutable?
(16 answers)
Closed 7 years ago.
Java string pool coupled with reflection can produce some unimaginable result in Java:
import java.lang.reflect.Field;
class MessingWithString {
public static void main (String[] args) {
String str = "Mario";
toLuigi(str);
System.out.println(str + " " + "Mario");
}
public static void toLuigi(String original) {
try {
Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
stringValue.set(original, "Luigi".toCharArray());
} catch (Exception ex) {
// Ignore exceptions
}
}
}
Above code will print:
"Luigi Luigi"
What happened to Mario?
What happened to Mario ??
You changed it, basically. Yes, with reflection you can violate the immutability of strings... and due to string interning, that means any use of "Mario" (other than in a larger string constant expression, which would have been resolved at compile-time) will end up as "Luigi" in the rest of the program.
This kinds of thing is why reflection requires security permissions...
Note that the expression str + " " + "Mario" does not perform any compile-time concatenation, due to the left-associativity of +. It's effectively (str + " ") + "Mario", which is why you still see Luigi Luigi. If you change the code to:
System.out.println(str + (" " + "Mario"));
... then you'll see Luigi Mario as the compiler will have interned " Mario" to a different string to "Mario".
It was set to Luigi. Strings in Java are immutable; thus, the compiler can interpret all mentions of "Mario" as references to the same String constant pool item (roughly, "memory location"). You used reflection to change that item; so all "Mario" in your code are now as if you wrote "Luigi".
To explain the existing answers a bit more, let's take a look at your generated byte code (Only the main() method here).
Now, any changes to the content's of that location will affect both the references (And any other you give too).
String literals are stored in the string pool and their canonical value is used. Both "Mario" literals aren't just strings with the same value, they are the same object. Manipulating one of them (using reflection) will modify "both" of them, as they are just two references to the same object.
You just changed the String of String constant pool Mario to Luigi which was referenced by multiple Strings, so every referencing literal Mario is now Luigi.
Field stringValue = String.class.getDeclaredField("value");
You have fetched the char[] named value field from class String
stringValue.setAccessible(true);
Make it accessible.
stringValue.set(original, "Luigi".toCharArray());
You changed original String field to Luigi. But original is Mario the String literal and literal belongs to the String pool and all are interned. Which means all the literals which has same content refers to the same memory address.
String a = "Mario";//Created in String pool
String b = "Mario";//Refers to the same Mario of String pool
a == b//TRUE
//You changed 'a' to Luigi and 'b' don't know that
//'a' has been internally changed and
//'b' still refers to the same address.
Basically you have changed the Mario of String pool which got reflected in all the referencing fields. If you create String Object (i.e. new String("Mario")) instead of literal you will not face this behavior because than you will have two different Marios .
The other answers adequately explain what's going on. I just wanted to add the point that this only works if there is no security manager installed. When running code from the command line by default there is not, and you can do things like this. However in an environment where trusted code is mixed with untrusted code, such as an application server in a production environment or an applet sandbox in a browser, there would typically be a security manager present and you would not be allowed these kinds of shenanigans, so this is less of a terrible security hole as it seems.
Another related point: you can make use of the constant pool to improve the performance of string comparisons in some circumstances, by using the String.intern() method.
That method returns the instance of String with the same contents as the String on which it is invoked from the String constants pool, adding it it if is not yet present. In other words, after using intern(), all Strings with the same contents are guaranteed to be the same String instance as each other and as any String constants with those contents, meaning you can then use the equals operator (==) on them.
This is just an example which is not very useful on its own, but it illustrates the point:
class Key {
Key(String keyComponent) {
this.keyComponent = keyComponent.intern();
}
public boolean equals(Object o) {
// String comparison using the equals operator allowed due to the
// intern() in the constructor, which guarantees that all values
// of keyComponent with the same content will refer to the same
// instance of String:
return (o instanceof Key) && (keyComponent == ((Key) o).keyComponent);
}
public int hashCode() {
return keyComponent.hashCode();
}
boolean isSpecialCase() {
// String comparison using equals operator valid due to use of
// intern() in constructor, which guarantees that any keyComponent
// with the same contents as the SPECIAL_CASE constant will
// refer to the same instance of String:
return keyComponent == SPECIAL_CASE;
}
private final String keyComponent;
private static final String SPECIAL_CASE = "SpecialCase";
}
This little trick isn't worth designing your code around, but it is worth keeping in mind for the day when you notice a little more speed could be eked out of some bit of performance sensitive code by using the == operator on a string with judicious use of intern().

Passing value of a string in an array to a method in java? [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 9 years ago.
thank you for taking the time to read this. Sorry if my question is dumb, I tried searching but couldn't quite figure out why I'm having this problem. I was trying to test some code for another application, but I'm having issues. Perhaps I just don't understand arrays properly.
I have a method named halfStepUp in a class named Transpose that, for testing purposes, should return "c#" if given "c" and "d#" if given "d". This is the code:
public class Transpose{
public static String halfStepUp(String note){
String n = null;
if (note == "c") n = "c#";
if (note == "d") n = "d"#;
return n;
}
}
I have the following code in my main method:
String [] scale = new String[2];
scale[0] = "c";
scale[1] = "d";
System.out.println(Transpose.halfStepUp(scale[0]));
This prints "null." What am I doing wrong?
I know the method works because if I use
System.out.println(Transpose.halfStepUp("c"));
It works fine. The solution is probably embarrassingly easy but I couldn't find a good way to word it when searching for help.
Thanks again for reading, and any answers are greatly appreciated!
To add a little more info to the answers you already got:
Java has two types of storage. One is the stack, which includes variable names and their values. One is the heap, that is just a huge collections of free-floating objects.
Now, if you're working with primitive types (like int, boolean or char), assigning a variable like
int myInt = 1;
pushes that variable on thje stack - the name is myInt, the value is 1.
If you, however, have an object (like strings are), assigning a variable does a little bit more.
String myString = "Hey!";
now creates an object (instance of String) somewhere on the heap. It has no name there, only some address in the memory where it can be found.
In addition to that, it pushes a variable on the stack. The name is myString - and the value is the address of the object on the heap.
So why is this relevant to comparing variables? Because == compares values of variables. ON THE STACK, that is. SO if you compare primitive types, everything works as expected. But if you're comparing Objects, == still only compares the values of the variables - which is, in that case, the addresses to the objects. If the addresses are the same, it returns true. That does mean, both variables point to the same object. If the addresses are different, == returns false., Without ever looking at the heap, where the objects really are.
An example:
String a = new String("Hey!");
String b = a;
if (a == b) System.out.println("true");
else System.out.println("false");
will echo "true" - because both variables contain the same object.
String a = new String("Hey!");
String b = new String("Hey!");
if (a == b) System.out.println("true");
else System.out.println("false");
will echo "false" - because you have two objects on the heap now, and a points to the one, while b points to the other. So while the contents of both objects may be the same, the contents of a and b on the stack are different.
Therefore, to compare any object, always use .equals() if you want to compare contents, not instance-equality.
[Addendum]:
With strings, this is even more complicated. As you already found out already,
String a = "Hey!"; // mention the difference to the example above:
String b = "Hey!"; // I did NOT use the `String()` cosntructor here!
if (a == b) System.out.println("true");
else System.out.println("false");
will actually give you "true". Now why is THAT? One might think that we still create two objects. But actually, we are not.
String is immutable. That means, once a String has been created, it cannot be modified. Ever. Don'T believe that? Take a look!
String myString = "test"; // we create one instance of String here
myString += " and more"; // we create another instance of String (" and more")
// and append that. Did we modify the instance stored in
// myString now? NO! We created a third instance that
// contains "test and more".
Therefore, there is no need to create additional instances of String with the same content - which increases performance, as Strings are widely used, in masses, so we want to have as few of them as possible.
To archive that, the JVM maintains a list of String Objects we already created. And every time we write down a String literal (that is something like "Hey!"), it looks in that lists and checks if we already created an instance that has that value. If so, it returns a pointer to that exact same instance instead of creating a new one.
And THIS is, why "Hey!" == "Hey!" will return true.
You should use the .equals() method when comparing strings, not ==. The == operator compares the references to see if they are pointing to the same underlying object. The .equals() method, compares the underlying objects to each other to see if they are semantically equivalent.
Try this instead: (edited from comments)
public class Transpose{
public static String halfStepUp(String note){
String n = null;
if ("c".equals(note)) n = "c#"; //using .equals as a string comparison
if ("d".equals(note)) n = "d#"; //not "d"#
return n;
}
}
The glitch is in this line:
if (note == "c") n = "c#";
This compares strings by address, not by value. Try using "c".equals(note) instead.
class Transpose{
public static String halfStepUp(String note){
String n = null;
if (note == "c") n = "c#";
if (note == "d") n = "d#";
return n;
}
}
public class TransposeTest {
public static void main(String... args) {
String [] scale = new String[2];
scale[0] = "c";
scale[1] = "d";
System.out.println(Transpose.halfStepUp(scale[0]));
}
}
working code

Why do I get different results when comparing strings after using different concatenation in Java?

i was working on the basic java program and i found verry funny thing which i am sharing with you. foo() gives output (s==s1) = false and bar gives (s==s1) = true.
I want to know why this happens.
public class StringTest
{
public static void main(String[] args){
foo();
bar();
}
public static void foo(){
String s = "str4";
String s1 = "str" + s.length();
System.out.println("(s==s1) = " + (s1==s));
}
public static void bar(){
String s = "str4";
String s1 = "str" + "4";
System.out.println("(s==s1) = " + (s1==s));
}
}
In the latter case, the compiler optimizes the string concatenation. As this can be done at compile time, both reference the same constant string object.
In the former case, the length() call can't be optimized during compile time. At runtime, a new string object is created, which is not identical to the string constant (but equal to it)
The string catenation in bar() can be done at compile time, because it's an expression composed of nothing but compile-time constants. Although the length of the String s is obviously known at compile time, the compiler doesn't know that length() returns that known value, so it won't be used as a constant.
When you write a line of code like this:
String s1 = "str" + "4";
then the compiler is smart enough to optimize this to:
String s1 = "str4";
Literal strings in Java are managed in a string pool. When you have two literal strings that have the same content (such as s and s1 in your second example), then just one String object will be created which will be shared by the two variables.
The == operator in Java checks if two variables refer to the same object. Since there is only one String object in the second example, s == s1 will be true.
String s1 = "str" + s.length();
String s1 = "str" + "4";
In first case s.length() will return a value of type int, In second case The type is String
Even though the number is 4 in both the cases but types are not the same :)
It probably has to do with the fact that foo() is probably creating an new String instance in s.length()(.toString()), where as bar() is just concatenating a constant. I don't know the nitty gritty of it, but my gut tells me it in that direction
If I needed to guess I would say that the java compiler performs some optimization onto bar(). At compiletime it is clear that "str" + "4" can be replaced by "str4" which (since Strings are immutable objects) is indeed the very same object as "str4"-String used for the s-initialization.
Within foo() the optimization is not that streight forward. In general the value s1-variable cannot be predicted very easily (indeed this example is quite streight forward). So the java compiler will produce two different variables for s and s1.
The "==" operator does not compare the value of the Strings! It checks whether these are the same Objects. To compare the values of the Strings use the "equals" method like this:
String s = "str4";
String s1 = "str" + s.length();
System.out.println("(s==s1) = " + (s1.equals(s2));
You should try playing with intern method of String class. Java keeps something like dictionary where all different strings are stored. When you create a string object which can be evaluated at compile time, Java searches it in its dictionary. If it founds the string, it stores only a reference to this string (which is actually returned by intern method).
You should notice that:
"str4" == ("str" + "str4".length()) returns false, but
"str4" == ("str" + "str4".length()).intern() returns true, because the only "wrapper" is a different object.

Need to change ResultSet.getString(Value)---- conditionally

I have a query and a resultset
I do this
while (rs.next())
{
String string = rs.getString(ColumnName);
if (String == "certainvalue")
{
//perform action
}else {
//do nothing
}
My problem is that the if condition doesn't seem to be working.... even though I know "certainvalue" is in the result set, it never evaluates to true, and it never performs the action---- I am confused as to why that is...
is it because i am using a while loop?? or is it because resultsets are just wierd,, ,what is going on???
Java can't compare strings with ==. What you have to do is use the equals method of the String.
if (string.equals("certainvalue")) {
perform action
}
It looks you're using Java. In that case, the == operator compares if the two objects are the same, not if they represent the same value.
Rewrite the test :
if ("certainvalue".equals(string)) { doStuff(); }
(You might consider "a".equals(b) to be equivalent to b.equals("a"), but the first form protects you from a NullPointerException if there is no value for the row in the database.)
String is an object. You can't do a comparison that way (you are trying to compare object reference).
If you are use java(aren't you?) try:
String string = rs.getString(columnName);
if (string.compareTo("anotherString") == 0){
}
You can use operator == just for primitive types (like int).

Categories

Resources