I'm looking into the method overloading of Java.
Take the next sample :
public static void main(String[] args) {
Object object = "some String";
System.out.println(object.getClass().getSimpleName());
System.out.println(belongsToAllowedTypes(object.getClass().cast(object)));
String string = "another String";
System.out.println(belongsToAllowedTypes(string));
}
public static boolean belongsToAllowedTypes(Object value) {
return false;
}
public static boolean belongsToAllowedTypes(String value) {
return true;
}
I'm expecting an output like :
String
True
True
Just because I get the String class and cast the Object to that class before I invoke the method.
But no luck, I'm getting false in the second println.
So it's still processed as an Object (however the class is String)
If I change it to :
System.out.println(belongsToAllowedTypes(String.class.cast(object)));
I do get a True.
Can anyone explain this behavior?
The method to call is determined at compile time. So with object having type Object the type of
object.getClass().cast(object))
at compile time is an Object independent of the dynamic content of object.
Related
I am unable to grasp the concept of Method references in case of instance methods in Java
For example in the example below, the compiler is giving error in the list line.
I have seen the examples of String::toUpperCase.
I am confused over the point that
(1) String is a class and toUpperCase is instance method. Java allows String::toUpperCase
(2) Why it is not allowing in my case:- AppTest::makeUppercase
package mja;
import java.util.function.Function;
public class AppTest {
public String makeUppercase(String source){
return source.toUpperCase();
}
public void printFormattedString(String string, Function<String, String> formatter){
System.out.println(formatter.apply(string));
}
public static void main(String[] args) {
AppTest appTest = new AppTest();
String source = "Hello World!";
// Below statement compiled successfully
appTest.printFormattedString(source, appTest::makeUppercase);
// Getting error that non-static method can't be referenced from static context
appTest.printFormattedString(source, AppTest::makeUppercase);
}
}
Why it is not allowing AppTest::makeUppercase?
The short answer is that AppTest::makeUppercase isn't valid "reference to an instance method of an arbitrary object of a particular type".
AppTest::makeUppercase must implement interface Function<AppTest, String> to be valid reference.
Details:
There are 4 kinds of method references in Java:
ContainingClass::staticMethodName - reference to a static method
containingObject::instanceMethodName - reference to an instance method of a particular object
ContainingType::methodName - reference to an instance method of an arbitrary object of a particular type
ClassName::new - reference to a constructor
Every single kind of method reference requires corresponding Function interface implementation.
You use as a parameter the reference to an instance method of an arbitrary object of a particular type.
This kind of method reference has no explicit parameter variable in a method reference and requires implementation of the interface Function<ContainingType, String>. In other words, the type of the left operand has to be AppTest to make AppTest::makeUppercase compilable. String::toUpperCase works properly because the type of parameter and type of the instance are the same - String.
import static java.lang.System.out;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
class ReferenceSource {
private String value;
public ReferenceSource() {
}
public ReferenceSource(String value) {
this.value = value;
}
public String doInstanceMethodOfParticularObject(final String value) {
return ReferenceSource.toUpperCase(value);
}
public static String doStaticMethod(final String value) {
return ReferenceSource.toUpperCase(value);
}
public String doInstanceMethodOfArbitraryObjectOfParticularType() {
return ReferenceSource.toUpperCase(this.value);
}
private static String toUpperCase(final String value) {
return Optional.ofNullable(value).map(String::toUpperCase).orElse("");
}
}
public class Main {
public static void main(String... args) {
// #1 Ref. to a constructor
final Supplier<ReferenceSource> refConstructor = ReferenceSource::new;
final Function<String, ReferenceSource> refParameterizedConstructor = value -> new ReferenceSource(value);
final ReferenceSource methodReferenceInstance = refConstructor.get();
// #2 Ref. to an instance method of a particular object
final UnaryOperator<String> refInstanceMethodOfParticularObject = methodReferenceInstance::doInstanceMethodOfParticularObject;
// #3 Ref. to a static method
final UnaryOperator<String> refStaticMethod = ReferenceSource::doStaticMethod;
// #4 Ref. to an instance method of an arbitrary object of a particular type
final Function<ReferenceSource, String> refInstanceMethodOfArbitraryObjectOfParticularType = ReferenceSource::doInstanceMethodOfArbitraryObjectOfParticularType;
Arrays.stream(new String[] { "a", "b", "c" }).map(refInstanceMethodOfParticularObject).forEach(out::print);
Arrays.stream(new String[] { "d", "e", "f" }).map(refStaticMethod).forEach(out::print);
Arrays.stream(new String[] { "g", "h", "i" }).map(refParameterizedConstructor).map(refInstanceMethodOfArbitraryObjectOfParticularType)
.forEach(out::print);
}
}
Additionally, you could take a look at this and that thread.
String::toUpperCase
is short version of
text -> {
return text.toUpperCase();
}
is again short version of
new Functon<String, String> (String text) {
Override
public String apply(String text) {
return text.toUpperCase();
}
}
so when you want AppTest::myMethod
you need
public class AppTest {
public String myMethod(){
return this.toString();
}
public void printFormattedString2(AppTest appTest, Function<AppTest, String> formatter){
System.out.println(formatter.apply(appTest));
}
public static void main(String[] args) {
AppTest appTest = new AppTest();
appTest.printFormattedString2(appTest, AppTest::myMethod);
}
}
because whole version looks so
appTest.printFormattedString2(appTest, new Function<AppTest, String>() {
#Override
public String apply(AppTest text) {
return text.makeUppercase2();
}
});
For simplicity, let us edit your class as below.
public class AppTest {
private String name;
public AppTest(String name){ this.name = name; }
public String makeUppercase() { //I have removed the argument here!!
return this.name.toUpperCase();
}
psvm main(){
AppTest appTest = new AppTest("Hello");
Stream.of(appTest).map(AppTest::makeUppercase).forEach(System.out::println);
//Here makeUppercase works of objects of type AppData similar to how String::toUpperCase works on object of type String!
}
}
This is accepted. Why?
Here, AppTest::makeUppercase is an instance method that operates on this instance of AppTest.
Why was yours not working?
appTest.printFormattedString(source, AppTest::makeUppercase);
This was not working because you are required to pass an implementation of Function. And, makeUpperCase() Function was not accessible from a non-static context since the method makeUpperCase() works on objects of type AppData. So, you need AppData instance to call this method!
Maybe you should change your method to be static and use it like this,
appTest.printFormattedString("Hello", AppTest::makeUppercase);
Why is the following code working?
appTest.printFormattedString(source, appTest::makeUppercase);
Because, you created an instance of AppTest and accessing the makeUppercase method (which is the implementation) and passing it as an argument to printFormattedString.
You need objects of a particular type to access the non-static method. But, You do not need objects of a particular type to access the static method.
String::toUpperCase works on instances of String. But you cannot access this method without having a String object to work on. Refer my comment in the code block to understand this better.
Java has the unfortunate allowed syntax of calling static methods on an instance, although this comes with a warning.
From the following example it seems that the compiler ignores the instance, and calls the static method directly according to the compile time class as evidenced by it succeeding though a null reference.
However from the following example, it seems that the compiler still does type checking on the instance,
class Example
{
static void f1() {}
public static void main(String[] args) {
Example example1 = null;
example1.f1(); // works OK
((Example) null).f1(); // works OK
((Example) (Object) ("")).f1(); // throws cast exception
}
}
Is this true? Why can't the compiler ignore the run time instance on what is on the left side of a static call?
The ClassCastException is thrown before the method is even called.
You do
String str = "";
Object o = (Object) str;
Example e = (Example) o; // exception
e.f1(); // never reached
Compiler needs to know which f1() method is called. So it needs to cast left side.
class Example
{
static void f1() {}
public static void main(String[] args) {
Example example1 = null;
((Example2) (Example) (Object) ("")).f1();
}
}
class Example2{
public void f1(){}
}
Can any one explain?
When we are overloading a constructor with different parameters one having data type object and other having data type string, and when we are creating the object of this class with providing input parameter as null it is calling the constructor with string as input parameter but not the constructor having input parameter as Object. Since Object is the super class of String, can any one tell me why it is calling constructor with input parameter string?
Class A
{
public A(Object o)
{
System.out.println("Object Drawn");
}
public A (String o)
{
System.out.println("String Drawn");
}
public static void main(String args[])
{
new A(null);
}
}
Output:- String Drawn
It always calls the most specific matching method or constructor. If it didn't you would always call Object and overloading it would be pointless.
This approach is using in Java and C++
I have an enum like:
enum TEST {
TEST1, TEST 2;
public abstract <T> String stringify (T input);
}
I need to add a constant specific method , something like stringify.
This method will take different types of inputs (for each enum). Can I do that? Eclipse is not letting me do it ..something like:
enum TEST {
TEST1(
public <Float> String stringify (Float input){
return String.valueOf(input);
}
)
}
You can't do it with enums, but you can simulate this behaviour with generic class:
public abstract class TEST<T> {
public static final TEST<Float> TEST1 = new TEST<Float>() {
public String stringify (Float input){
return String.valueOf(input);
}
};
public abstract <T> String stringify(T input);
}
you can do it. but it's not clear what the benefit is:
enum TEST {
TEST1 {
public <Float>String stringify(Float input) {
System.out.println("TEST1");
return String.valueOf(input);
}
},
TEST2 {
public <Integer>String stringify(Integer input) {
System.out.println("TEST2");
return String.valueOf(input);
}
},
TEST3 {};
public <T>String stringify(T input) {
System.out.println("super");
return "";
}
public <Integer>String stringify2(Object input) {
System.out.println("non generic");
return String.valueOf(input);
}
}
public class Main{
public static void main(String[] args) {
for(TEST test:TEST.values()) {
System.out.println(test.stringify(new Float(1.23)));
System.out.println(test.stringify(new Integer(42)));
System.out.println(test.stringify(new Double(4.56)));
}
for(TEST test:TEST.values()) {
System.out.println(test.stringify2(new Float(1.23)));
System.out.println(test.stringify2(new Integer(42)));
System.out.println(test.stringify2(new Double(4.56)));
}
}
}
Think of each Enum value as a class. So yes, the enums can have methods, just like a class does -- they all have the same methods though.
http://download.oracle.com/javase/tutorial/java/javaOO/enum.html
Look at the Planet example.
Also note that the enum itself can have static methods....(just like a class)
No, you can't make each enum constant implement an abstract method but require a different type of input than the other enum constants. If you could, what would happen if you were given an instance of your TEST enum (you don't know what constant it is) and tried to call stringify on it? What type would you pass it?
Edit: Given what you've said about these enums being used to decode strings into objects, it seems to me you have several options:
You could get a String representation of each decoded object by just calling toString() on it.
You could add a set of overloaded static method on the enum class itself, called stringify(Float) and stringify(Double), etc. Then you could just call TEST.stringify(value) and if there were a stringify method for the value's type, it'd work fine.
I imagine there are other options as well.
You can't use generics with enums, because the enum constants themselves are already the concrete (singleton) instances. At instance level, the generics must already be concrete.
So, I would stringly recommend going with one of the alternatives given in the other answers.
If you must do it in an enum, you could consider the following, which at least gives you a runtime means of type checking, including ClassCastExceptions. You won't have any support from the compiler though.
public enum TestEnum {
Test1(Float.class),
Test2(Integer.class),
Test3(String.class);
private final Class<?> iInputType;
private TestEnum(final Class<?> pInputType) {
iInputType = pInputType;
}
public Class<?> getInputType() {
return iInputType;
}
public String stringify(final Object pInput) {
return String.valueOf(iInputType.cast(pInput));
}
}
Test Code:
System.out.println(TestEnum.Test1.stringify(1.23f));
System.out.println(TestEnum.Test2.stringify(42));
System.out.println(TestEnum.Test3.stringify("foo"));
// but:
// System.out.println(TestEnum.Test1.stringify("foo")); // -> ClassCastException!
for (TestEnum test : TestEnum.values()) {
for (Object input : new Object[]{1.23f, 42, "foo"}) {
if (test.getInputType().isAssignableFrom(input.getClass())) {
System.out.println(test.stringify(input));
}
}
}
I have the following code :
public class Main {
public void method(Object o)
{
System.out.println("Object Version");
}
public void method(String s)
{
System.out.println("String Version");
}
public static void main(String args[])
{
Main question = new Main();
question.method(null);//1
}
}
why is the result is "String Version" ? and why there is a compiler error if the first method takes a StringBuffer object ?
Another case : if the first method takes a StringBuffer object and I write question.method("word");the result will be "String Version" . Why ? why there is no compiler error ?
The JAVA spec says that in cases like this, the most specific function will be called. Since String is a sub type of Object - the second method will be called.
If you change Object to StringBuffer - there is no specific method since StringBuffer is not a sub type of String and vice versa. In this case the compiler does not know which method to call - hence the error.
When looking at the other case :
package com.snamellit;
public class Main {
public void method(Object o) {
System.out.println("Object Version");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
Main question = new Main();
question.method("word");
}
}
If the first method tqkes a StringBuffer and the second a String, there is no confusion possible as "word" is a String and not a StringBuffer.
In Java the identity of a function/method is dependent on 3 things : the name, the type pf the parameters (aka the argument signature) and the classloader. Since both types have a different argument signature the compiler can easily choose the right one and does not raise an error.