Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.ArrayList#add automatically converts Ruby's Time to java.util.Date #2049

Closed
danielribeiro opened this issue Oct 15, 2014 · 7 comments

Comments

@danielribeiro
Copy link

Hi,

This happens using java 7 and jruby 1.7.16. The reproduction code is:

puts JRUBY_VERSION
require 'java'
time = Time.now
ar = java.util.ArrayList.new
ar.add time
puts time
puts time.class
puts ar[0]
puts ar[0].class

Output:

screen shot 2014-10-15 at 6 49 21 pm

Cheers,

  • Daniel
@rtyler
Copy link

rtyler commented Aug 7, 2015

This still exists in 9k, this is at the least confusing to the user, I'm not sure if it's a bug or not.

@kares kares added this to the Won't Fix milestone Apr 9, 2016
@kares
Copy link
Member

kares commented Apr 9, 2016

in generic cases (when the target signature is simply java.lang.Object) JRuby converts its internal classes to their "best" Java equivalents. for Time objects the sensible default is java.util.Date (hoping it will change one day as Java 8 is adopted as a minimum). you can however provide a hint with an explicit to_java conversion if you want the ruby object: ar.add Time.now.to_java 'org.jruby.RubyTime'

@kares kares closed this as completed Apr 9, 2016
@daicoden
Copy link
Contributor

@kares Why don't you think this is a bug? Time is a pretty complex object...

Consider this...

require 'active_support/all'
1.second # 1 second
Time.now.beginning_of_day # works

list = java.util.ArrayList.new
list.add(1)
list.add(Time.now)

list.get(0).second # 1 second
list.get(1).beginning_of_day # undefined method `beginning_of_day' for #<Java::JavaUtil::Date:0x409986fe>

I don't think time is really a primitive, but if we're going to map it to a java object it should follow the same rule as strings and have all all method additions available when accessing it from the ruby side.

@kares
Copy link
Member

kares commented Apr 12, 2016

@daicoden yes that is "ugly". but there's a bigger picture and your example is actually minor to how much value would be lost if we passed down RubyTime directly every time we see a java.lang.Object type. Java libraries would have to assume JRuby's internal org.jruby.RubyTime object.

we simply need some common ground here. that being set on Java 8 we can do much better with the new Date and Time APIs. we still need to maintain compatibility but it could work with a switch to not use legacy java.util.Date as a default conversion. thus if this bothers you that much, feel free to start :)

@enebo
Copy link
Member

enebo commented Apr 12, 2016

@kares @daicoden I wonder if we couldn't solve this with a require somehow. By requiring something we change semantics of how Time is mapped. If you do not do that require it is existing semantics.

@daicoden
Copy link
Contributor

thanks for the reply @kares, my thoughts on a switch below.

@enebo @kares a switch or require would work, though I would be nervous turning it on as it could break other libraries which accidently relied on the current behavior for some reason.

A simple solution without breaking the API would be changing the error message for java.util.DateTime to reference an issue number. The problem with the bug is that it takes a long time to comprehend that Time is an outlier and it makes you nervous about using java collections in ruby (though that's one of the best things about jruby).

Another idea and expanding on my "time is not really primitive but..." comment.
What if we convert java.util.Date to RubyTime on the way out? Even though it's not a primitive, if we treat it the same way as String and Integer then the example above would work.

The inverse would break if inserting a java.util.Date, but I think it would be better to have all the implicit conversions behave the same way. Note java.util.Integer behaves like this already.

require 'active_support/all'
one = java.lang.Integer.new(1)

list = java.util.ArrayList.new
list.add(one)

list.get(0).second # 1 second

Are there any other objects that behave like Time? If not - then my vote is make Time behave like String, Integer, Boolean, etc.

@kares
Copy link
Member

kares commented Apr 13, 2016

thanks @daicoden - appreciate your idea but we were actually debating avoiding those conv in the past by duck typing Java types to seem like Ruby ones (e.g. java.lang.String). as conversions are not perfect :

java.lang.Integer -> Fixnum -> by default converts to java.lang.Long

while Ruby's Time is a beast it make sense for java.util.Date to get some of its methods (obviously not everything that AS adds) in this case it would make some sense as well. probably doesn't get any better - with dropping Java 7 support things could get more clean as JRuby can adopt JDK's classes instead of JODA.

we're also considering a java.util.ArrayList.new! kind of syntax for disabling type coercion with Java objects in Ruby land.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants