Win a copy of OCP Oracle Certified Professional Java SE 11 Programmer I Study Guide: Exam 1Z0-815 this week in the Programmer Certification forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Junilu Lacar
  • Jeanne Boyarsky
  • Bear Bibeault
Sheriffs:
  • Knute Snortum
  • Devaka Cooray
  • Tim Cooke
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Ron McLeod
  • Carey Brown
Bartenders:
  • Paweł Baczyński
  • Piet Souris
  • Vijitha Kumara

Input parameter for DoubleToIntFunction

 
Ranch Hand
Posts: 77
2
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys. I'm studying with the OCP Practice test book and from chapter 14 ex.30 I got a question related to DoubleToIntFunction.

Because I don't understand how is possible that a lambda implementing that functional interface can receives either Double or double (while the signature is applyAsInt​(double value) ) whereas with other functional interfaces you should to respect the input parameters from the lambda to match with the signature of the interface. For example:



On the other hand with DoubleToIntFunction:



Thanks!
 
Bartender
Posts: 3611
151
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
hi Pablo,

bit of a mystery for me. At first, when I typed and ran your code, I got indeed a compiler error. But after some messing around, it suddenly worked for me. So I tried a generic version of your code:

works fine. Can you try it again?
 
Master Rancher
Posts: 3399
33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Java often allows us to convert an int into an Integer, or a double to a Double, or vice versa.  Boxing and unboxing.  This is useful when some data was originally one type, but you need to convert it quickly to another. And that's what you (Pablo) are doing in your second example:

Deposit was declared as a Double in one line, and converted to a double in the next.  Fine, no problem.  A bit silly since we could have made deposit a double to begin with, but Java allows this - there could have been a good reason for a previously-declared variable to be a Double, but now you want it to be a double.  Fine, Java will let you do that.

However:

Here, you're declaring f as an Integer, and passing the lambda to a method that expects a function that takes an int.  The key is, you're doing this in the very same line that you declared f as an Integer, and you then immediately want it to be an int instead.  Java says no, wait a minute, this doesn't make sense.  They could have designed Java to accept this too, I suppose, but they also might have ben a little reluctant to accept this use case.  After all, if you've just declared the variable in that line, why do you immediately need it converted to a different type?  There's not a good reason to need this, in my opinion.  And, there may have been a reason why you really wanted it to be an Integer rather than int.  Sometimes we create performance problems by needlessly boxing and unboxing too many times, and so it's sometimes worthwhile to make sure you know what type a variable has in a given context, to ensure you aren't accidentally causing another conversion without realizing it.  

So, they didn't include this situation in the list of places where you could automatically box or unbox.  Instead they assume that if you do provide a type in the lambda argument list (which you didn't need to do in the first place), then they will assume that's the type you actually want to use.  And if it doesn't make sense, well, better to ave a compilation error force you to clarify what you really wanted.
 
Piet Souris
Bartender
Posts: 3611
151
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Mike
then how do you explain that it works for me (OP's original code and the snippet I gave)? The auto(un)boxing works as it should.
 
Mike Simmons
Master Rancher
Posts: 3399
33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not sure, Piet.  I know why it works with the code you showed in your post - you've introduced a new version of rest() that accepts a ToIntFunction, rather than an IntFunction, which means that now it can directly accept an Integer rather than an int, so the problem Pablo originally experienced now goes away.  Are you saying that Pablo's code now works for you, even without your new rest() method?  Maybe you thought you deleted the new version, but there's a class file lying around that still lets you use it?  I don't know. But, until you wrote that new code, your compiler was correctly telling you that Pablo's code won't compile.  If you completely delete your new code, then Pablo's code will, again, not compile.

It's also possible that you're now asking about the return value aspect of the code, which I did not address.  (I was only talking about input parameters.) I see  in the last line iof your main(), you're returning an explicitly cast Integer in a context where it expects an int, and that works.  So yeah, that's a bit different from the situation for input parameters.  Let's see...

Quoth JLS 15.27.3:

A lambda expression is congruent with a function type if all of the following are true:

The function type has no type parameters.

The number of lambda parameters is the same as the number of parameter types of the function type.

If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.

If the lambda parameters are assumed to have the same types as the function type's parameter types, then:

If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.

If the function type's result is a (non-void) type R, then either (i) the lambda body is an expression that is compatible with R in an assignment context, or (ii) the lambda body is a value-compatible block, and each result expression (§15.27.2) is compatible with R in an assignment context.


I highlighted the two relevant sections here, which show how the rules for parameters and return values in a lambda are different.  For the input parameters, Pablo used an explicitly typed lambda.  So the types need to be the same as the parameter types of the target function.  Which means exactly the same.  But for return types, they instead say, the expression or return statement type must be compatible with the target return type R, in an assignment context.  That "compatible in an assignment context" is the language that allows you to use boxing or unboxing.  They didn't say that for input param types, only for return values.

Why this inconsistency?  I think it's because they'd already written rules for using boxing and unboxing in return statements, prior to introducing lambdas.  And I guess they didn't want to change them when lambdas came along.  But, the rule for how to handle explicitly declared input param types in a lambda, well that was new - and they probably couldn't think of any valid reason anyone would need to box or unbox at that point.  Because they could instead either (A) declare the right type, exactly, or (B) not bother declaring the input param type at all, since you don't have to, in a lambda.

These are just my offhand guesses about their thinking though; I don't have any privileged info here.
 
Piet Souris
Bartender
Posts: 3611
151
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks, Mike. Cow for the efforts.

And I did change the IntFunction to ToIntFunction. I guess that was one of my messings around and I didn't notice it...
 
Pablo Napoli
Ranch Hand
Posts: 77
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks guys for your replies!. They were very useful for me to understand that there is no a logical cause to this happens but it is rather a "design resolution". Anyway it could be something very tricky if OCP designers test try to play with this.

Anyway, I like to add my opnion. I think Java is not such a clear language about conversions between different types. I expose my thought because I'd like to know your opinion (you guys are like jedi masters for me) so if I'm wrong I could change it. But this is not the only case tht I've seen.
For instance, it's a tricky example that I could never clarify:


In this code it's possible getting a Double by the addition of n+d and then convert it to int.

But here I can't make the convertion:


For sure we can say that in the fist one we made an addition but anyway I think it's not clear because for different cases I cannot cast from a wrapper to a primitive.
 
Marshal
Posts: 66237
250
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't use new Double(123.456). Use Double d = Double.valueOf(123.456); or Double d =123.456;
If you try arithmetic on Double objects with + or similar, the objects will undergo unboxing conversion. which can later on be cast to an int etc. But it is not possible to combine unboxing with narrowing or widening conversions.That is why your second code block didn't compile.
 
You are HERE! The other map is obviously wrong. Better confirm with this tiny ad:
Java file APIs (DOC, XLS, PDF, and many more)
https://products.aspose.com/total/java
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!