for example, i have this class:
public class Col {
static void test(int a)
{
System.out.println("int");
}
public static void main(String args[])
{
Col.test(12); //1
Col.test((byte)12); //2
Col.test((long)100); //3
}
}
and now me intresting how algoritm work this code.
I think, that this steps:
1 line - all correct call method with int param, perfect.
2 line - call method with byte param...oooops. what do? Java try widening byte to int? Its true?
3 line call method with long param... again ooops. what do? convert long to int java can't, because loss of accuracy. its try? And in result - Exception.
Than I add this:
public static void test(Object a)
{
System.out.println("Object");
}
and if a call:
Col.test((long)100);
all correct, no Exception
so, what the relation between primitive type long and Object?
Yes, there's an implicit conversion from byte to int, but no implicit conversion from long to int (because of the likelihood of losing information).
In the third case, you're using autoboxing which will convert a long (primitive type) to a Long (class type).
You can see that by changing the body of test to:
public static void test(Object a)
{
System.out.println(a.getClass());
}
It will then print out class java.lang.Long.
Your first example shows conversion of primitive types. The second shows boxing and unboxing, which is - in brief - a convenient conversion between primitive type (like long) and their wrapper classes (java.lang.Long in this case).
Overloading is implementing methods that have the same name but different parameters. Here we have two methods
static void test(int a){}
static void test(Object a){}
and call it with test((long) 100). The first method can't be called, because the JVM won't narrow a long to an int without explicit casting. But the JVM (Version 1.5+) can convert the long value to a Long (autoboxing) and test(Long.valueOf((long) 100)) is a good match for the second method.
This is because auto-boxing feature.
Actually you have promoted the primitive to long and while calling test method automatically it is seaching for its equivalent type hence it is calling test(Object a).You can see like this Col.test(new Integer(12));this will also call test(Object a).Also you can refer this link
Determining if an Object is of primitive type
public static void test(Object obj) {
if (obj instanceof Integer) {
System.out.println("Integer");
} else if (obj instanceof Double) {
System.out.println("Double");
} else if (obj instanceof Float) {
System.out.println("Float");
} else if (obj instanceof Long) {
System.out.println("Long");
}
}
All java primitives have corresponding boxed "types" that are actual classes. In you example, long has a corresponding class Long. This class extends from Object.
What you have experienced is boxing and unboxing.
It is a feature introduced in Java 5. Its called Autoboxing. In this a primitive type is converted to Object (in your case long to Long). See this link for details on Autoboxing.
Related
I am a bit rusty and got stuck on sort of elementary problem. I want my method printBinary to take in a Long or Integer. and base on the input type I want to call the respective Object's toBinaryString() method.
Now I know there are alternative ways like method overload, I can probably create two methods both call printBinary but one takes Long and the other takes Integer. But if I want to do this in one method how can I achieve that?
public static <T extends Object> void print(T arg){
System.out.println(arg);
}
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
Explanation
Your write
public static <T extends Number> void printBinary(T arg)
so you declared T to be something which extends Number. Very important, note that nothing more is known about T. So you can only safely assume it is Number but nothing more (without checking, for example using instanceof).
The next thing is that you write
// requires long
print(Long.toBinaryString(arg));
// requires int
print(Integer.toBinaryString(arg));
but both methods don't require Number, they require long and int. Take a look at their documentation (Long, Integer).
Even if we consider boxing, Java would only be able to convert Long into long and Integer into int (or vice versa). But not Number to Long or Number to Integer. A Number is not necessarily a Long or Integer. It could, for example, also be a Double. And you don't know what T is, you only know it is at least of type Number.
Utility method
So what you will need to do is explicitly convert the arg to long and int. Therefore you can use utility methods provided by Number, namely Number#longValue and Number#intValue:
print(Long.toBinaryString(arg.longValue()));
print(Integer.toBinaryString(arg.intValue()));
Cast after instanceof
The other possibly is to cast them. As you have checked with instanceof that T truly is of type Long or Integer you can safely cast it:
print(Long.toBinaryString((Long) arg));
print(Integer.toBinaryString((Integer) arg));
After the cast Java will automatically convert Long to long and Integer to int, this is called unboxing. Or to make it more explicit, call the unboxing method by yourself:
print(Long.toBinaryString(((Long) arg).longValue()));
print(Integer.toBinaryString(((Integer) arg).intValue()));
Notes
To be precise the cast to Integer is unchecked with your current code. You need to also check arg instanceof Integer, safer would thus be:
if (arg instanceof Long) {
// Cast to Long is safe
} else if (arg instanceof Integer) {
// Cast to Integer is safe
} else {
// Neither Long nor Integer, for example Double
throw new IllegalArgumentException("Only Long and Integer supported.");
}
If you want your method
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString(arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
to work, you need put casts when calling toBinaryString(art) method, for example like this
public static <T extends Number> void printBinary(T arg){
if(arg instanceof Long){
print(Long.toBinaryString((Long) arg)); //this throws an error incompatible types: Number cannot be converted to long
}else{
print(Integer.toBinaryString((Integer) arg)); //this throws an error incompatible types: Number cannot be converted to int
}
}
But still, there is one problem with the code that you posted. As it was said by #Jacob H, declaration like <T extends Number> means that method will accept parameters of any class that extends the Number class. So it will work if parameter will be of type Integer or Long, but will fail when for example Float value will be passed.
Unfortunately toBinaryString method is not implemented by Number class, but is a method provided by subclasses like Integer and Long.
So for your case, it would be probably better to implement two separated methods that take parameters of different types - Long and Integer.
In case you really want to use this generic notation, but not handle input of other types than Integer and Long, you will probably need to itroduce some kind of parameter type validation. But still, definition of method that takes Number type parameter and does not handle some of it's subclasses and some does might be confusing and will look like a bad design.
You just want to use <Number>, <T extends Number> means "some type T that extends Number", so it expects either Long or Integer, but not both. <Number> means "Number, or any type extending Number"
The best solution is to use method overload. Using Number, the method will also accept doubles.
If you really want to use Number, you don't need generics. Rewrite the methods like this
public static void print(Object arg)
public static void printBinary(Number arg)
I was reading a java book, and came up with this example
public class Main {
public static void main(String[] args) {
play(4); // DOES NOT COMPILE
play(4L);
glide(1,4); // THIS ONE COMPILES
}
public static void play(Long l) { }
public static void play(Long... l) { }
public static void glide(long i, long j) {}
}
but didn't quite get the idea why doesn't java cast the int to long and call the first method, because it does the cast for the method with 2 parameters?
Because the method glide uses parameters of type long, and java easily casts int to long, but the first method uses parameter of type Long, which is the wrapper class for long type, not int, that's why the first method doesn't compile
The third method, glide, is an example of a widening cast from an int to a long which is done automatically.
The first method, play(Long l), accepts a Long (capital L) object. Primitives cannot be cast to objects which is why your first example doesn't compile, but the compiler will convert a primitive to its equivalent object via "autoboxing" which is why play (4L) works.
Note these rules:
You CANNOT widen and then box. (An int can't become a Long.)
You CANNOT widen from one wrapper type to another. (IS-A fails.)
You can box and then widen. (An int can become an Object, via Integer.)
Here is what I know about overload resolution in java:
The process of compiler trying to resolve the method call from given
overloaded method definitions is called overload resolution. If the
compiler can not find the exact match it looks for the closest match
by using upcasts only (downcasts are never done).
Here is a class:
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(Object a){
System.out.println(10);
}
}
As expected the output is 10.
However if I change the class definition slightly and change the second overloaded method.
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(String a){
System.out.println(10);
}
}
The output is 8.
Here I am confused. If downcasting was never to be used, then why did 8 get printed at all? Why did compiler pick up the TestOverLoad method which takes int as an argument which is a downcast from Integer to int?
The compiler will consider not a downcast, but an unboxing conversion for overload resolution. Here, the Integer i will be unboxed to an int successfully. The String method isn't considered because an Integer cannot be widened to a String. The only possible overload is the one that considers unboxing, so 8 is printed.
The reason that the first code's output is 10 is that the compiler will consider a widening reference conversion (Integer to Object) over an unboxing conversion.
Section 15.12.2 of the JLS, when considering which methods are applicable, states:
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing [...]
In Java, resolving methods in case of method overloading is done with the following precedence:
1. Widening
2. Auto-boxing
3. Var-args
The java compiler thinks that widening a primitive parameter is more desirable than performing an auto-boxing operation.
In other words, as auto-boxing was introduced in Java 5, the compiler chooses the older style(widening) before it chooses the newer style(auto-boxing), keeping existing code more robust. Same is with var-args.
In your 1st code snippet, widening of reference variable occurs i.e, Integer to Object rather than un-boxing i.e, Integer to int. And in your 2nd snippet, widening cannot happen from Integer to String so unboxing happens.
Consider the below program which proves all the above statements:
class MethodOverloading {
static void go(Long x) {
System.out.print("Long ");
}
static void go(double x) {
System.out.print("double ");
}
static void go(Double x) {
System.out.print("Double ");
}
static void go(int x, int y) {
System.out.print("int,int ");
}
static void go(byte... x) {
System.out.print("byte... ");
}
static void go(Long x, Long y) {
System.out.print("Long,Long ");
}
static void go(long... x) {
System.out.print("long... ");
}
public static void main(String[] args) {
byte b = 5;
short s = 5;
long l = 5;
float f = 5.0f;
// widening beats autoboxing
go(b);
go(s);
go(l);
go(f);
// widening beats var-args
go(b, b);
// auto-boxing beats var-args
go(l, l);
}
}
The output is:
double double double double int,int Long,Long
Just for reference, here is my blog on method overloading in Java.
P.S: My answer is a modified version of an example given in SCJP.
widening beats boxing, boxing beats var-args. In your example, the widening cannot happen, so the boxing it's applied and Integer is unboxed. Nothing unordinary.
Actually in the second example no downcasting is occurred. There occurred the following thing -
1. Integer is unwrapped/unboxed to primitive type int.
2. Then the TestOverLoad(int a) method is called.
In main method you declare Integer like -
Integer i = 9;
Then call -
test.TestOverLoad(i);
Whereas, you have 2 overloaded version of TestOverLoad() -
TestOverLoad(int a);
TestOverLoad(String a);
Here the second overloaded version of TestOverLoad() takes completely different argument String. Thats why Integer i is unboxed to a primitive type int and after that the first overloaded version is called.
All objects in Java extend the class Object, including the class Integer. These two class have the following relationship: Integer "is a(n)" Object because Integer extends Object. In your first example, the method with Object parameter is used.
In the second example, no methods are found that accept an Integer. In this case Java uses what is called auto-unboxing to resolve the Integer wrapper class to a primitive int. Thus, the method with the int parameter is used.
While accepted answer of #rgettman leads to very right source, more precisely, §15.12.2.2 and §15.12.2.3 of the JLS Section 15.12.2 discuss the applicability, not the resolution - what the OP asked for. In the example the OP provided both testOverLoad methods are applicable, .i.e. will be successfully resolved in the absence of another one.
Instead, 15.12.2.5. Choosing the Most Specific Method discusses the resolution of the applicable methods.
It reads:
One applicable method m1 is more specific than another applicable
method m2, for an invocation with argument expressions e1, ..., ek, if
any of the following are true:
...
m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter
types T1, ..., Tn, the type Si is more specific than Ti for argument
ei for all i (1 ≤ i ≤ n, n = k).
So, in the first example, provided by OP, for parameter i of type Integer method testOverLoad(Object a) is more specific than testOverLoad(int a).
This is happening due to Widening and Narrowing Type casting
Widening means a small type can be accommodated in a larger type without any loss of information.
Widening Typecasting is automatic.
That means a byte value can be automatically casted to short, int, long or double.
byte->short->int->float->double
Widens from left to right.
Type Casting in Java
Hope this answers your question!
you can check with one more example :
public class HelloWorld {
void show(String c){
System.out.println("int double overloaded method");
}
void show(Object c){
System.out.println("double int overloaded method");
}
}
here the you will get : double int overloaded method
I try to dive deeply into the Java Generics and I've come across a problem described by the following sample code.
public static void test(Object o) {
System.out.println("Hello Object!");
}
public static void test(Integer i) {
System.out.println("Hello Integer!");
}
public static <T> void test(Collection<T> col) {
for (T item : col) {
System.out.println(item.getClass().getSimpleName());
test(item);
}
}
public static void main (String[] args) throws java.lang.Exception
{
Collection<Integer> ints = new ArrayList<>();
ints.add(1);
test(ints);
}
The output of the sample is
Integer
Hello Object!
All the types are obviously known at compile time. As far as I understand, Java holds only a single compiled copy of each method (unlike C++) and because no other constraints are given about the parameter T, it's forced to call the generic Object implementation.
My question is - is there any way to call the "Hello Integer" method for integers without overloading the test method for Collection<Integer> and without using runtime type checking?
It cannot be done. Due to type erasure type Collection<T> will be resolved to type Collection<Object>. Erasure of Generic Methods
You can observe the same behavior without generics.
Object o = new Integer(5);
test(o); // will output "Hello Object!"
Overloaded methods are resolved using compile-time rather than run-time type information. See JLS: https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.9
No, you can't do what you ask for, for the reasons you gave. Type erasure means that the overload is resolved once for all type variables, and it has to resolve to Object.
I didn't even know this was doable, but I saw while perusing some code online a method with a signature like this:
public List<Void> read( ... )
... What? Is there ever a reason to do this? What could this List even hold? As far as I was aware, it's not possible to instantiate a Void object.
It is possible that this method signature was created as a by-product of some generic class.
For example, SwingWorker has two type parameters, one for final result and one for intermediate results. If you just don't want to use any intermediate results, you pass Void as the type parameter, resulting in some methods returning Void - i.e. nothing.
If there were a method List<V> returnAllIntermediateResults() in SwingWorker with Void as the type parameter V, it would have created a method just like you posted in your question.
The code would be perfectly valid. You can instantiate any implementation of the List interface (e.g. ArrayList) with type parameter Void. But the only value a Void type can have is null. So the list could not hold anything else but nulls, if the implementation allows null elements.
One case in which it may be useful is if you wanted to return a collection of return values from a function. Say
static List<T> forEach(Func<A,T> func, List<A> items) {
List<T> ret = new List<T>();
for(int i = 0; i< items.length; i++) {
ret.add(func.call(items[i]);
}
return ret;
}
public static void main() {
...
List<Void> boringResult =
forEach(
new Func<Void, Integer> {#override Void call(Integer i) {...}});
}
Not that useful but you could see a case where it was required.
List<Void> is weird. It can only have null elements, since you can't create an object of type Void. I don't think there is a practical use for such a thing.
Void is part of java.lang. It's not a special keyword or anything. It's a "pseudo-type" (according to the docs) used to as a place-holder to represent the Class object corresponding to void, as in Class<Void>. From the docs for Class:
The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
The Void class exists mainly for the sake of the last part of this, so you can write:
Class<Void> voidType = void.class; // == Void.TYPE
just like you can write:
Class<Integer> intType = int.class; // == Integer.TYPE
I agree, it's odd.
I can see a use for it if you want to extend a generic class and return void from a method. I've bumped into a case were I want to use int and had to use Integer because java generics don't like primitive types.
public interface ObjectUserPool<E, T> {
public E useObject(T o);
}
public class NonReturningObjectUserPool extends ObjectUserPool<Void, Integer> {
public Void useObject(Integer i);
}
I think this is what the java API is saying, though to be honest I can't really find a use for NonReturningObjectUserPool.