I have a class
class Configuration {
// various stuff
#Override
public String toString() {
// assemble outString
return outString;
}
}
I also have another class
class Log {
public static void d(String format, Object... d) {
// print the format using d
}
}
The Log class works perfectly fine, I use it all the time. Now when I do this:
Configuration config = getConfiguration();
Log.d(config);
I get the compiler error The method d(String, Object...) in the type Log is not applicable for the arguments (Configuration). I can solve this:
Log.d("" + config); // solution 1
Log.d(config.toString()); // solution 2
My problem: How is this different? In the first solution, the compiler notices that it has to concatenate two Strings, but the second one is a Configuration. So Configuration#toString() is called and everything is fine. In the compiler error case the compiler sees that a String is needed, but a Configuration is given. Basically the same problem.
Needed: String
Given: Configuration
How are these cases different and why is toString not called?
While designing the language, someone decided that when a programmer appends an arbitrary object to a string using the + operator, they definitely want a String, so implicitly calling toString() makes sense.
But if you call an arbitrary method that takes a String with something else, that is simply a type error, exactly what all that static typing is supposed to prevent.
The line
"" + config
gets translated to something like
StringBuilder sb = new StringBuilder("");
sb.append(config);
where the second line calls
StringBuilder.append(Object obj);
This method calls obj.toString() to get the String representation of the object.
On the other hand, the first parameter of Log.d must be a String, and Java doesn't automatically call toString() to cast everything to a String in that case.
One of the common use of toString(), is print() and println() methods of PrintStream, as in:
System.out.print(object);
System.out.println(object);
Basically, these two methods will call toString() on the passed object. This is one of the Polymorphism's benefits.
Nice Question...
But,
Compiler does not call a method to match formal parameters. it simply tries to cast the objects if possible.
But when you use the "+" operator the compiler executes the toString() method of its arguments (in case they are objects) by default.
In one case you are passing an object argument to an operator which expects objects.
In the earlier case you are passing an object argument to a function which expects string.
Basically function/operator signature is different.
It is almost incidental [in the context of this question] that .tostring called when + is applied. It takes an object and does something.
For all you know, you might be passing in object when string is required by mistake. So it can't blindly do .tostring()
You are passing Configuration class object argument in case 1 but in the case 2 , you are passing string argument . so no error occures.
Related
According to Oracle Documentation, the String::compareToIgnoreCase is also a valid method reference, my question is that compareToIgnoreCase is not a static method, in other words, compareToIgnoreCase must be attached to a specific String instance. So how does JDK know which instance of String I refer when I use String::compareToIgnoreCase ?
Consider the following example using toUpperCase which is also an instance method.
It works in this case because the Stream item that is being handled is of the same type as the class of the method being invoked. So the item actually invokes the method directly.
So for
Stream.of("abcde").map(String::toUpperCase).forEach(System.out::println);
the String::toUpperCase call will be the same as "abcde".toUpperCase()
If you did something like this:
Stream.of("abcde").map(OtherClass::toUpperCase).forEach(System.out::println);
"abcde" is not a type of OtherClass so the OtherClass would need to look like the following for the stream to work.
class OtherClass {
public static String toUpperCase(String s) {
return s.toUpperCase();
}
}
String::compareToIgnoreCase is not used such as str1.compareToIgnoreCase(str2) would be.
It actually is used as a comparator.
E.g. you could compare it to
Arrays.sort(someIntegerArray, Collections.reverseOrder())
but in this case it would be
Arrays.sort(someStringArray, String::compareToIgnoreCase)
It is like there is an additional parameter, the actual instance, involved.
Example for String::compareToIgnoreCase:
ToIntBiFunction<String, String> func = String::compareToIgnoreCase;
int result = func.applyAsInt("ab", "cd"); // some negative number expected
We get a ToIntBiFunction - a two parameter function returning int - since the result is an int, the first parameter correspond to this of compareToIgnoreCase and the second function parameter is the parameter passed to compareToIgnoreCase.
maybe a bit easier:
ToIntFunction<String> f = String::length; // function accepting String, returning int
int length = f.applyAsInt("abc"); // 3
length does not accept any parameter, but the first argument of the function is used as the instance length is called on
The examples above are very abstract, just to show the types involved. The functions are mostly used directly in some method call, no need to use an intermediate variable
for example in Scanner we have
obj.next()
but we can call another method after next()
obj.next().charAt(0)
how can I make similar thing for example
obj.getName().toLowerCase()
What you have observed – with examples like obj.getName().toLowerCase() – is that when the return type of a method call is itself some other object, then you can immediately call a new method on that newly returned object.
Here's another example: String s = String.class.getName().toLowerCase();. This example could be rewritten like so:
Class<String> stringClass = String.class;
String name = stringClass.getName();
String s = name.toLowerCase();
Both of the one-line and multi-line version of this code result in a String object, referenced by s, which contains the value "java.lang.string".
Note that chaining method calls together is not possible if the return type isn't an object, such as an integer value. For example, here's a method call that results in a primitive long value, which isn't an object, so you can't call any methods on that result – that is, something like millis.hashCode() isn't possible.
long millis = System.currentTimeMillis();
To address your primary question finally: you can create this same behavior by creating methods that return objects instead of primitives (int, long, byte, etc.).
I am trying to use mockito to mock a method. However the class I am injecting mocks with calls the method twice while sending in two different objects of the same type, but depending the values in the object determine the output of the method.
So, for example, If I am trying to mock
public ArrayList<example> attemptToMock(testObject testing)
Lets sat type testObject has a string value in it.
So if the string value in testObject is "OK" then attemptToMock should output an array of two objects in it. If testObject string value is "NO" then the Array list sent out only has one Object.
How to I write a test to handle a call so that a class can call attemptToMock twice, within the same method, and I can mock out its output it so depending on the values within testObject. I can mock it to send out different arrays.
A few options:
Override equals and hashCode on your object (TestObject). This is only feasible if all of the values on your object are predictable, and may be more work than other solutions, but if you need to write equals and hashCode anyway (for Map and Set behavior, for example) this is a reasonable solution.
// Mockito compares with objects' equals(Object) methods by default.
when(collaborator.attemptToMock(object1)).thenReturn(array1);
when(collaborator.attemptToMock(object2)).thenReturn(array2);
Write a Hamcrest matcher and use that to match the arrays. This acts as a compact analogue to equals for a specific case, and is especially handy if you need to change behavior based on the same value in many tests.
public class IsATestObjectWithValue extends TypeSafeMatcher<TestObject> {
private final String expectedValue;
public IsATestObjectWithValue(String expectedValue) {
super(TestObject.class);
this.expectedValue = expectedValue;
}
#Override public void matchesSafely(TestObject object) {
// object will never be null, but object.value might.
return expectedValue.equals(object.value);
}
}
Now you can write an equivalent match as above:
when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("OK")))
.thenReturn(array1);
when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("NO")))
.thenReturn(array2);
Use an Answer, as wdf described. Anonymous inner Answers are common and pretty concise, and you get access to all of the arguments. This is especially good for one-off solutions, or if you want to explicitly and immediately fail the test if an invalid value (testObject.value) is passed in.
As a last resort, if the order of the calls is predictable, you can return multiple values in sequence.
when(collaborator.attemptToMock(any(TestObject.class)))
.thenReturn(array1).thenReturn(array2);
when(collaborator.attemptToMock(any(TestObject.class)))
.thenReturn(array1, array2); // equivalent
Either of the above lines will return array1 for the first call and array2 for the second call and all calls after it, regardless of the parameter. This solution will be much more brittle than your original question asks for--it'll fail if the call order changes, or if either of the calls is edited out or repeated--but is sometimes the most compact solution if the test is very temporary or if the order is absolutely fixed.
You can access the parameters passed into a mocked method invocation and vary the return value accordingly by using the Answer interface. See the answer to this question, and the docs for Answer.
Basically, the only weird/non-obvious thing going on here is that you have to downcast the parameters to the type you are expecting. So, in your case, if you are mocking a method that takes a single 'TestObject' parameter, then you'll have to do something like this inside of your 'answer' implementation:
Object[] args = invocation.getArguments();
TestObject testObj = (TestObject) args[0];
if ("OK".equals(testObj.value)) {
return new ArrayList(value1, value2);
} else if ("NO".equals(testObj.value)) {
return new ArrayList(singleObject);
}
I have a small question, given the following snippet:
StringBuilder stringBuild = new StringBuilder(3);
stringBuild.append("hello");
System.out.println(stringBuild+2); // if I omit the (+2) bit hence only stringBUild it works
Does it call automatically toString() on object only in some circumstances? (circumstances: either no operation at all or a previous string contatanation)
The compiler never calls toString() on a method argument implicitly.
What you are probably thinking of, is that there is an overload of System.out.println which takes an Object (rather than a String) - this is the method that the compiler would link to. And this particular implementation of the method calls toString on the Object passed in (at runtime). That's just code though, it's nothing to do with compiler behaviour.
So passing in an Object to System.out.println "works". Passing in stringBuild+2 simply doesn't compile - there's no + operator on StringBuilder which takes an int. (And you can't create one yourself as Java doesn't allow for operator overloading.)
As ADTC and tom point out, there is implicit String conversion with the second argument to string concatenation (the + operator for strings). So while stringBuild doesn't have a + operator, stringBuild.toString() would, and you could call stringBuild.toString()+2.
In java is there a way to call a void method from a constructor.I try something like this but get an error message that the compiler can not find symbol method printThis(java.lang.String):
public class Date{
public Date(String inString){
String s = inString;
String b;
b.printThis(s);
}
public void printThis(getString)
{
System.out.printf(System.out.printf( new SimpleDateFormat("MM/dd").format(new SimpleDateFormat("MM/dd").parse(getString) ) );
}
You want printThis(s) - the complier is looking for a printThis method on the String instance, which does not exist.
There are LOTS of errors in the code as presented. These are the ones I spotted.
public class Date{
Problem: you are missing package declaration means this will be in the default package. That's a bad idea.
Problem: you are using a class name that is the same as commonly used classes in the standard class library. That's a bad idea.
public Date(String inString){
String s = inString;
String b;
b.printThis(s);
Error: The code attempts to invoke a method in the String API called printThis(...). No such method exists. You should probably get rid of b and just call printThis(s)
Error: The code attempts to use an uninitialized local (b) and this will give a compilation error (if you "fixed" the previous error by changing the type of b to something that did have a printThis method).
Problem: It is bad practice for a constructor to invoke a method on the object being constructed if there is any possibility that it might be overridden in a subclass. The problem is that the overriding method (from the subclass) might be called on the object before the superclass initialization has completed. It is safe to call static or private methods.
}
public void printThis(getString) {
Error: There is a syntax error in the declaration. Change getString to String getString.
Problem: The choice of parameter name is (IMO) nonsensical. What is a "get string"???
System.out.printf(System.out.printf(
new SimpleDateFormat("MM/dd").format(
new SimpleDateFormat("MM/dd").parse(getString) ) );
Error: Compilation error: the parentheses don't balance.
Error: Compilation error: the first argument to printf must be a String or a Locale. In your code, the first argument in the outer call is a PrintStream instance.
Error: System.out.printf(System.out.printf( is nonsensical. You almost certainly should use just System.out.println or System.out.print. If you do use a printf method you have to supply a format string in the syntax specified in the PrintStream javadocs. (This is NOT the same as the syntax used for date formats!!!)
}
Error: missing '}' to complete class.
Problem: Your code style needs a lot of work. If you can swear on a bible that nobody else is ever going to have to read your code (!), then I suppose its OK. Otherwise, this kind of stuff is unacceptable. If this was homework, I'd dock you 50% of your marks straight off for making no attempt to get the style correct.
You have used printThis() as a method of String. If you want to print the date you might want
printThis(s);
It's not generally a good idea to use the same class name (Date) as the JDK library class
The following lines are not going to work:
String b;
b.printThis(s);
What the above code is doing is attempting to call the printThis method on the String object called b.
Since a String.printThis method does not exist, the compiler returns the error message saying that it could not find the method.
What is probably intended is the following:
printThis(s);
The above will call the printThis method of the current instance.
You're getting that error because you're trying to call printThis() on object b, which is a String. You want:
public Date(String inString) {
printThis(inString);
}
Just FYI, it's generally inadvisable to name classes the same as JDK classes (like Date). Also the assignment you're doing of inString doesn't really achieve anything. Perhaps your code is a simplification of what you're doing but I thought I'd mention it anyway.