How java casts parameters to pass to a method? - java

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.)

Related

java method takes a T extends Number

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)

Primitive vararg parameters in method overloading

Primitives are at it again, breaking rules, I learned before. Well not technically primitive but composed of them.
I learned that whenever there's no method more specific than rest, compile time error occurs as it happens here.
public static void caller(){
z5(); // Error. Neither Integer, nor String is more specific
z5(null); // Error for the same reason
}
public static void z5(Integer...integers){
System.out.println("Integer z5 called");
}
public static void z5(String...strings){
System.out.println("String z5 called");
}
Now comes primitives into the picture.
public static void caller(){
z1(null); // Error cuz [I, [J, [F all are subclass of Object.
z1(); // SURPRISINGLY works and calls the int one. WHY?
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(long...longs){
System.out.println("long z1 called");
}
public static void z1(float...floats){
System.out.println("float z1 called");
}
Expected compile time errors occurs here.
public static void caller(){
z1(null); // Error
z1(); // Error
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(boolean...bools){
System.out.println("bool z1 called");
}
Now my question is, int[], float[], or any array of primitives are not primitive types then Why are they treated differently than other reference types?
--UPDATE--
#john16384 You don't think I read your "Possible duplicate" Varargs in method overloading in Java
Top answer there says You cannot combine var-args, with either widening or boxing. Besides I forgot to mention, OP's code posted there, works fine on my jdk 7.
What exactly is going on which works for (int...is) & (float...fs) but not for (Integer...is) & (Float...fs) and not for (int...is) & (boolean...bool)
Quote from the JLS about varargs invocations when multiple methods are applicable:
15.12.2.5. Choosing the Most Specific Method
If more than one member method is both accessible and applicable to a
method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time error. In cases such as an
explicitly typed lambda expression argument (§15.27.1) or a variable
arity invocation (§15.12.2.4), some flexibility is allowed to adapt
one signature to the other.
The important part here is how methods are defined to be more specific. It basically says that int... is more specific than long... because any values you could pass to the first method could also be passed to the second method.
This will also apply to the case where you pass no arguments. int... will be the most specific (it will even see byte... as more specific!).
public static void main(String[] args) {
bla();
}
private static void bla(long... x) {}
private static void bla(int... x) {}
private static void bla(short... x) {}
private static void bla(byte... x) {} // <-- calls this one
The reason you get an error when also creating an overload boolean... is that it now is ambigious which one to call, and the compiler stops before getting to the point where it has to pick the most specific method.

Java issue with var-args and boxing

I have a question related to the following code snippet:
class VarArgsTricky {
static void wide_vararg(long... x) {
System.out.println("long...");
}
static void wide_vararg(Integer... x) {
System.out.println("Integer...");
}
public static void main(String[] args) {
int i = 5;
wide_vararg(i, i, i); // needs to widen and use var-args
Long l = 9000000000l;
wide_vararg(l, l); // prints sucessfully "long..."
}
}
The first call to wide_vararg fails to compile(saying that the method is ambigous) while the second compiles just fine.
Any explanations about this behaviour?
Thanks!
The first wide_vararg call is ambiguous because the compiler could either:
widen the ints to longs, and call the first wide_vararg method, or
autobox the ints to Integers, and call the second wide_vararg.
It doesn't know which it should do, however, so it refuses to compile the ambiguous method call. If you want the first call to compile, declare i as an Integer or a long, not an int.
When a var-arg method is invoked, the parameters get converted into an array of that type at compile time.
In the first call, the parameters get converted to an int[]. As all arrays in Java are direct sub types of the Object class, the concept of primitive widening does not apply in which case, both the overloads become equally applicable because long[] and Integer[] are at the same level. Hence the ambiguity

why primitive type will call first rather than wrapper classes?

public class A {
public void test(Integer i) {
System.out.println("In Wrapper Method");
}
public void test(int i) {
System.out.println("In primitive Method");
}
public static void main(String args[]) {
A a = new A();
a.test(5);
}
}
When I will call test method from main and pass integer argument, then it will call the method which accept primitive type as argument. I just want to know that why it call primitive type method rather than the method who accepts wrapper class as argument? Is there any rule, which java follow to call methods?
Thanks,
As a rough "rule of thumb", Java uses the closest matching method for the declared types of the argument expressions when choosing between different overloads. In this case test(int i) is closer than test(Integer i) when the method is called as test(5), because the latter requires a type promotion (auto-boxing) to make the actual argument type correct.
In fact the decision making process is rather more complicated than this, but the "rule of thumb" works in most cases.
In this case, you are using the literal value 5, which is a primitive int in Java. Any bare number literal such as that in Java is a primitive. To call the other method would require passing new Integer(5) instead. The autoboxing that was introduced in Java 5 can blur the lines between the two, but they are still distinct from each other.
Not really an answer, but when overloading it shouldnt matter which method is called. In this case calling either method if the value is an Integer or int the result should be the same. Overloading should only be used to convert values to some other form, or as a mechanism to provide defaults.
String.valueOf() is a good example of converting inputs to a String.
a.test(5) // test(int i)
Compiler understands it as a primitive type. If you want run the other method you should send
a.test(new Integer(5)) // test(Integer i)
because Java select the closest matching method to run
You have passed 5 as primitive type so it will call the method that accepts primitive so if you want to call the one that accepts 5 as object then you have to first covert it to object as below
int a = 5;
Integer b = new integer(5);

Java. Overloading method

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.

Categories

Resources