public class helloworld {
public static void main(String[] args) {
String text = "Hello World";
l(text);
int n = 0;
l("--------------------------");
l(n);
}
public static void l(Object obj) {
System.out.println(obj);
}
}
I wrote this simple program in Java and it worked. Now I am confused that if all the data types (int, char, double etc.) come under Object, then why do we specify which data type we want to accept when we pass values?
I mean we can always use the data type Object as used in the function l. Is there a specific reason why people don't always use Object as their data type to pass values?
There is an implicit conversion defined between all primitive types and their respective object counterparts:
int -> Integer
char -> Character
etc...
This is called autoboxing.
Is there a specific reason why people don't always use "Object" as their data type to pass values?
Since Java is strongly typed, you cannot do a whole lot with Object.
E.g. try this:
static Object add(Object a, Object b) {
return a + b; // won't compile
}
This is because methods, operators, etc. available to use depend on the static type of the variable.
println can accept Object because it only needs to call the toString method. If you only need the limited functionality provided by the methods in Object, then sure, you can use it as a type. This is rarely the case, however.
For the primitives you mentioned, they are not really objects, they will simply be boxed to their representation as an object. An int would become an Integer, a long would become a Long etc.
Read this article about Autoboxing in java.
As for your question
Is there a specific reason why people don't always use "Object" as
their data type to pass values?
If you specify Object as the parameter of your method you won't be able to call the methods the real object contains without doing a cast. For example, if you have a custom object AnyObject that contains a method anyMethod, you won't be able to call it without casting the object to AnyObject.
It will also be unsafe as you will be able to pass any type of object to a method which may not be designed to function properly with any of these types. A method containing only System.out.println is not representative of a real use case, it will work with any object simply because by default the println will call the toString method which is already defined in an Object.
While it does look like a function that appears to accept all types of parameters, you will have to deal with these
The function signature becomes less informative.
No more overloading
You have to do a lot of type checking and casting in the function body to avoid run time errors.
Although the method seemingly accepts all objects, you would never know the actual subset of them until you see the method definition.
The function body might end up having more code to eliminate the wrong types than for its real goal. For example, your function only prints the value. Imagine a function that predominantly does some integer operation.
Increases the probability of run time errors, as the compiler cannot throw errors for missing casts.
Related
I am writing some Junit tests in Android, and if i do this:
public void testSetId(){
Friend friend = new Friend(5);
assertEquals(5,friend.getId());
}
I get an ambiguous method call error.
Ambiguous Method Call:
Both AssertEquals(int, int) and
AssertEquals(Object, Object) match
Yet If i do this:
public void testSetId(){
Integer ID = 5;
Friend friend = new Friend(ID);
assertEquals(ID, friend.getId());
}
It works. I feel like the second function should be doing the exact same thing.
What is going on here?
Before Java 5 there was no autoboxing or auto-unboxing. This meant that if a method foo had a parameter of type Integer, the following did not compile
foo(5); // 5 needs to be autoboxed
Similarly, if a method bar had a parameter of type int, this did not compile
bar(new Integer(5)); // The Integer needs to be unboxed
When autoboxing and auto-unboxing were introduced, it was essential for existing code to continue to work in exactly the same way as before. Therefore when the compiler decides which method is being called, it first considers only the applicable methods that do not require autoboxing or auto-unboxing. Only if there are no such methods does the compiler consider methods requiring autoboxing and/or auto-unboxing.
Since getId returns an Integer, the Object, Object method can be called without autoboxing in the case when the first argument is also an Integer. However the int, int method can only be called by auto-unboxing the second parameter. Therefore in your second example, the Object, Object overload is selected on the first pass.
In your first example, you are trying to pass an int and an Integer. The int, int method applies only by auto-unboxing the second argument, while the Object, Object method applies only by autoboxing the first argument. Therefore the overload cannot be chosen on the first pass, and because neither method is more specific than the other (you'll need to look that last bit up) the compiler cannot choose between the two methods.
Overload resolution is extremely complicated, and I've actually simplified it quite a bit (there are also rules involving var-args). However in practice, you don't need to learn all these rules - if ever you need to tell the compiler which method to apply you can always include an explicit cast or casts:
assertEquals((Integer) id, friend.getId());
Below are the two lines of my code snippet:
List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin");
listDevs.sort(Comparator.comparing(String::length)); //This works fine
listDevs.sort(String::compareToIgnoreCase); //This works fine
But (out of expermient) when I try to write
listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
The compiler throws error
Cannot make a static reference to the non-static method
compareToIgnoreCase(String) from the type String
Similar happens to the below code
listDevs.sort(Comparator.comparing(String::compareTo));
I understand the error and that it works fine if I remove the Comparator.comparing (as shown above).
But my point is, how does this line works?
listDevs.sort(Comparator.comparing(String::length));
I believe I am missing something. I have read this thread. Is this the same scenario?
Comparator.comparing expects a Function which describes a comparable property of the elements. So String::length is sufficient as length() is a property of the String evaluating a String to an int (that’s why comparingInt is preferable here).
In contrast, String.compareToIgnoreCase and String.compareTo are comparison methods. They compare two String objects. So references to them are sufficient where a Comparator is expected, but not where a property Function is expected.
It’s like you have a factory saying “Gimme an engine, and we build a car for you” and you are trying to give them a complete car. While that existing car is valid where a car is expected, there is no sense in passing it to the factory to built a car.
Unfortunately, the current compiler implementation is very bad at reporting error with functional signatures. You will almost always see messages like “Cannot make a static reference to the non-static method …” when signatures mismatch.
The sort method expected a Comparator.
When you do this, you are indeed providing one.
listDevs.sort(Comparator.comparing(String::length));
Same happens here(but a bit non-intuitive):
listDevs.sort(String::compareToIgnoreCase)
listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above
That's exactly the definition of a Comparator - take two Strings and return an int.
The line that you say how come this works: listDevs.sort(Comparator.comparing(String::length)); is actually pretty simple.
Comparator.comparing takes a Function that transforms your input type into something that is Comparable. In your case takes a String and returns an Integer; which is Comparable.
JLS says Compile-Time Declaration of a Method Reference of ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways.
Given a targeted function type with n parameters, a set of potentially applicable methods is identified:
ReferenceType :: [TypeArguments] Identifier has two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a static method or an instance method.
A method reference expression of the form ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways. If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter with type of this compared to if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.
Comparator.comparing method accept a Function<T,R extends Comparable<? super R>>. when you use String::compareToIgnoreCase that will reports error,because it has two parameters one is implicit this another is a comparing string of method parameter, so it is more like a BiFunction<String,String,Integer> not a Function<String,Integer>.
BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase;
// you can't assign a BiFunction to a Function
// because one is incompatiable with another.
Function<String,Integer> function = comparator;
Stream.sort method accept a Comparator, and Comparator is more like a BiFunction<T,T,Integer> so it is compatiable with String::compareToIgnoreCase. on the other hand, they can be interchangeable. for example:
Comparator<String> primary = String::compareToIgnoreCase;
BiFunction<String, String, Integer> comparator1 = primary::compare;
Comparator<String> comparator2 = comparator1::apply;
you can using comparing(String::toLowerCase) instead, it is equalivent to String::compareToIgnoreCase, for example:
// String::compareToIgnoreCase
listDevs.sort(String::compareToIgnoreCase);
// comparing(String::toLowerCase)
listDevs.sort(comparing(String::toLowerCase))
I'd like to know if something like this is possible in Java (mix of C++ and Java ahead)
template<typename T> bool compare(Wrapper wrapper) {
if(wrapper.obj.getClass().equals(T.class))
return true
return false
}
To clarify, the function takes in an object which contains a java.lang.object, but I'd like to be able to pass that wrapper into this generic comparison function to check whether that object is of a particular type, ie
if(compare<String>(myWrapper))
// do x
No, it's not possible due to erasure. Basically, the compare method has no idea what T is. There's only one compare method (as opposed to C++, where there's one per T), and it isn't given any information about how it was invoked (ie, what the caller considered its T to be).
The typical solution is to have the class (or method) accept a Class<T> cls, and then use cls.isInstance:
public <T> boolean compare(Wrapper wrapper, Class<T> cls) {
return cls.isInstance(wrapper.obj);
}
// and then, at the call site:
if (compare(wrapper, Foo.class)) {
...
}
Of course, this means that the call site needs to have the Class<T> object. If that call site is itself a generic method, it needs to get that reference from its caller, and so on. At some point, somebody needs to know what the specific type is, and that somebody passes in Foo.class.
You cannot reference static members of a type parameter (such as you try to do in the form of T.class). You also cannot use them meaningfully in instanceof expressions. More generally, because Java generics are implemented via type erasure, you cannot use type parameters in any way at run time -- all type analysis is performed statically, at compile time.
Depending on exactly what you're after, there are at least two alternative approaches.
The first, and more usual, is to ensure that the necessary types can be checked statically. For example, you might parameterize your Wrapper class with the type of the object it wraps. Then, supposing that you use it in a program that is type-safe, wherever you have a Wrapper<String> you know that the wrapped object is a String.
That doesn't work so well if you want to verify the specific class of the wrapped object, however, when the class to test against is not final. In that case, you can pass a Class object, something like this:
<T> boolean compare(Wrapper<? super T> wrapper, Class<T> clazz) {
return wrapper.obj.getClass().equals(clazz);
}
That checks the class of the wrapped object against the specified class, allowing the method to be invoked only in cases where static analysis allows that it could return true.
You can actually combine those two approaches, if you like, to create a Wrapper class whose instances can hold only members of a specific class, as opposed to any object that is assignable to a given type. I'm not sure why you would want to do that, though.
I came across the following snippet of code that uses generics.
public class Generics<T> {
public static <T> T replaceIfNull(T objectToCheck, T defaultValue) {
return objectToCheck == null ? defaultValue : objectToCheck;
}
public static <T> boolean CheckIfNull(T objectToCheck) {
return objectToCheck == null ? true : false;
}
}
I am having a difficult time truly understanding how generics work, formed and used. I have a high level understanding, meaning that I know the definition of generics. And by the definition my interpretation of this code snippet is that replaceIfNull method checks for null values of any object and then returns a default value (whatever that is). And that CheckIfNull method is similar, in that it checks null value for any object.
But how does this work? Why is the method formed with <T>, which seems to be a type and then there is T following. I do not understand this syntax, <T> T means? And how does T become a type in the parameters? How come this method, for example, could not be written as
public static Object replaceIfNull(Object objectToCheck, Object defaultValue) {
return objectToCheck == null ? defaultValue : objectToCheck;
}
Thank you in advance for your clarification.
Let's start with answering your last question. The method rewrite with Object instead of generics has two drawbacks. First, it will not work with primitives (which might not be a real drawback in this case, since you are checking against null, and primitives cannot take null values, but still...). Second, it will require casting. If , for example, you use this method on two strings, like replaceIfNull(myString, "Default Value"), then you would expect to get a String as an output, right? But instead the method declared to be returning Object; so there is no way for compiler to know that it will return a String, and you will have to do casting every time you use it: `String result = (String) replaceIfNull(myString, "Default Value");' Generics were introduced specifically to fix this situation.
You can think of generics as templates; whatever type you put in the angle braces will be used later in the code whenever you use type parameter. So, <T> means: "Here will go some type; replace T with it everywhere in the code".
And the last question is about the method signature. I think here you mixed up two different methods of using generics - on a class level, and on a method level. Since you've already introduced the type parameter on the class level, there is no need to do it again on a method level, so I think you can safely remove <T> from method declaration.
I suggest you read a more detailed explanation on generics here: http://docs.oracle.com/javase/tutorial/java/generics/
The subject of Generics is very broad and you can read about it extensively on Oracle's website or on Stack Overflow.
Why is the method formed with <T>, which seems to be a type and then there is T following.
This is a generic method. The <T> declares a new type variable with no bounds.
// v return type
public static <T> T replaceIfNull(T objectToCheck, T defaultValue) {
// ^ new type variable ^ type variable used as a type
Within the method body, since the type variable has no bounds, T can, at most, be interpreted as Object. You're only going to have access to method declared in Object on expressions of type T.
Outside the method, ie. in invocation contexts, the type variable T will receive a concrete type value. That is, it will either infer it from its invocation context or it will be provided explicitly.
For example, in
replaceIfNull(someStringVar, otherStringVar);
the type variable T will be bound to String, so all usages of T will be interpreted as String. You could therefore do
String notNull = replaceIfNull(someStringVar, " not null ");
You could also provide the type argument explicitly
Generics.<String>replaceIfNull(nullVar, " not null ");
and now again the type variable will be bound to String.
Note that the type variable T declared at the type level
public class Generics<T>
is completely different from the type variable T declared in the method.
Trying to learn some basic operations using JAVA-Genric, i tried to make a generic class, which takes a number and does some operation on it and returns the value but its throwing error
Parent class :
public class HelloWorld{
public static void main(String []args){
MathsExec<Integer> my_obj = new MathsExec<Integer>();
int rst = my_obj.doAddition(100);
System.out.println("Solution is : "+rst);
}
}
Generic Class :
class MathsExec<T>{ //didn't extend NUMBER because m strictly sending int values
T doAddition(T nmbr){
int k=100;
T rst;
rst = nmbr+k;
return rst;
}
}
Error :
MathsExec.java:6: error: bad operand types for binary operator '*'
rst = nmbr+k;
^
first type: T
second type: int
where T is a type-variable:
T extends Object declared in class MathsExec 1 error
I understand why this error is coming(incompatible types for operation) but as per generics, type T should have been converted to Integer before doing the + operation...or is there some other explanation i should know????
P.S : please go easy, JAVA is not my strong suite!!
Generic types are not known in compile time. Therefore, you get that compilation error. And it makes sense, because there is no guarantee that your * operator will work on your generic type T.
A trivial but educational solution might be adding an interface called Multipliable and adding the abstract method multiply() to it, than calling that method in your doAddition method. BTW to do that you need to change your class definition as something like
class MathsExec<T extends Multipliable>
Additional clarifications:
We have to come to an understanding that you cannot use operators like * for classes, they are for primitive types only.
If you have to use generics and you have to do some operations on generic types, you have to keep your compiler happy and assure it that that Generic object, does have that method. And that is through T extends SomeClass.
If you want to practice generics without using interfaces or abstract classes or whatever, the most common use case is custom data structures. Where it is possible that you do not actually need many operations on the data that you are storing. You just put them in your structure.
// didn't extend NUMBER because m strictly sending int values
You can do that. int primitives will be boxed to Integer wrapper. But the issue is, arithmetic operators won't work on generic types. There may be workaround, but it wouldn't be worth of your effort. I would rather provide overloaded method for handling different primitive types.
BTW, you should take two arguments in the doAddition() method, and pass the value of k, that you're currently hard-coding. This will allow you to re-use this method in any other class too.
You should do an explicit cast from T to int if you know for sure that T will be casted down to an int. The compiler has no way of knowing if that will actually happen, so it will give you that error.