Fork me on GitHub
#java
<
2020-10-28
>
Jim Newton15:10:37

Hi everyone. I have some questions about objects which respond true to the clojure class? function. First of all what is the correct word to use to describe the set of objects for which class? returns true? I want to call them "classes" but sometimes people complain that interfaces are not classes.

andy.fingerhut15:10:37

The Java Virtual Machine specification (separate from the Java language specification, which specifies Java source code) uses the names classes and interfaces, e.g. you can find occurrences of both in the table of contents here: https://docs.oracle.com/javase/specs/jvms/se8/html/

andy.fingerhut15:10:01

The Java documentation for the class java.lang.Class says near the beginning: "Instances of the class `Class` represent classes and interfaces in a running Java application." -- https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html

Jim Newton15:10:50

interesing. so the word "Class" with a capital C includes classes and interfaces ?

Alex Miller (Clojure team)15:10:01

class is used both as a generic name (including interfaces) and as a specific thing (concrete classes distinct from interfaces) depending on context, but yes Class includes both

andy.fingerhut15:10:04

And the phrase "class and interface" and "class or interface" is sprinkled all over that last documentation page I linked.

Jim Newton15:10:06

the set of objects of class java.lang.Class ?

Alex Miller (Clojure team)15:10:19

there are also arrays, which are objects but somewhat outside typical classes, and primitives

Jim Newton15:10:40

it's hard to talk about the world with a limited number of words which different people attach different semantics to.

andy.fingerhut15:10:03

Hence the use of short phrases and definitions of terms within a document that needs to make precise distinctions that are not often made casually.

Jim Newton15:10:02

OK, 2nd question. Can someone give me an example of two interfaces (which I can reference by name in clojure such as java.lang.Comparable or .Serializable) which are incompatible in the sense that they both have a member with the same name and the same parameter types?

Jim Newton15:10:27

i.e. two interfaces which are guaranteed never to be simultaneously in the ancestors list of any one Class (with a capital C)

andy.fingerhut15:10:35

If no one knows, one way to attempt to answer the question is to run some code that scans the JVM's internal state to try to find all current instances of java.lang.Class, keeps only the ones that represent interfaces, and write some code that collects together all of their method names and signatures, looking for similar ones.

andy.fingerhut15:10:00

I have some Clojure code somewhere I can probably link to that uses a library written by someone else to look for all (or at least most) instances of java.lang.Class in a running JVM. I haven't written code to do the latter part, but it should be straightforward to write given a collection of instances of java.lang.Class

Jim Newton15:10:44

I tried to find the descendants of Object, but that doesn't work. If it did I could find all descendants, then fall all their ancestors. Since interfaces won't be descendants, but gathering all the ancestors would find a huge number of interfaces.

Jim Newton15:10:15

@andy.fingerhut indeed, given a collection of a large number of instances of java.lang.Class, yes I would have a good chance of finding such interfaces.

andy.fingerhut15:10:18

The JVM set of classes and interfaces is by design very dynamic, and they can be loaded/created by a myriad of different ClassLoader objects, and there are security mechanisms to try to hide the existence of some from different "places" in the system, so there are varying degrees of completeness you can achieve with such a list, depending on how you do it.

andy.fingerhut15:10:00

It might be an hour or three before I find the code I wrote (away from that laptop right now)

Alex Miller (Clojure team)15:10:15

you could skim the javadoc index

Alex Miller (Clojure team)15:10:29

like abort() is a "method in interface" for many interfaces, probably not all the same hierarchy

Alex Miller (Clojure team)15:10:16

http://java.net.http.WebSocket and javax.security.auth.spi.LoginModule

Alex Miller (Clojure team)15:10:13

probably pick any common verb :)

Jim Newton15:10:49

http://java.net.http.WebSocket seems to be both :abstract and also :interface

Alex Miller (Clojure team)15:10:59

all interfaces are abstract

Jim Newton15:10:13

ah ha, but not all abstracts are interfaces?

Alex Miller (Clojure team)15:10:44

abstract classes can't be instantiated

Alex Miller (Clojure team)15:10:12

they have to be subclassed and made non-abstract (usually by overriding abstract methods)

Jim Newton16:10:35

what does it mean if a Class is not :abstract, and not :interface, and not :final ?

Jim Newton16:10:56

does it work the same as abstract but can be instantiated?

andy.fingerhut16:10:27

If it is none of those, that is probably the most common kind of class that people learn to create in Java first. It is a class, not an interface. It is not declared abstract, so you can use new to construct instances of that class. It is not final, so you can define subclasses of it if you wish.

andy.fingerhut16:10:44

interface -> it is a Java interface (implies abstract, and I believe never final), abstract -> cannot construct instances of it. final -> cannot declare subclasses of it

Jim Newton16:10:47

ahhh, so these are common ?

andy.fingerhut16:10:03

Having none of those three flags present is common

Jim Newton16:10:34

do we have examples of those in clojure other than Object ?

Jim Newton16:10:57

Object is the one that I have seen. and I've made a special case of it. but if this is common, I need to understand it better, perhaps.

andy.fingerhut16:10:09

clojure.lang.PersistentVector

andy.fingerhut16:10:14

java.lang.Object is in every JVM, whether Clojure is running or not. It is the class from which all other classes are sub-classes (perhaps through a chain of multiple other intermediate classes)

Jim Newton16:10:58

great.. is there some class which inherits from clojure.lang.PersistentVector ?

andy.fingerhut16:10:18

If you go through the source directory here, you can probably find a bunch, all in the clojure.lang package: https://github.com/clojure/clojure/tree/master/src/jvm/clojure/lang

Jim Newton16:10:21

ie. a subclass of clojure.lang.PersistentVector ?

andy.fingerhut16:10:46

I do not know of any off hand. Perhaps not in Clojure itself, but some third party library very well might.

Jim Newton16:10:37

again if it were possible to walk the instances of java.lang.Class I could programmatically find such cases.

seancorfield16:10:01

@jimka.issy FWIW, most things that need to be "vector-like" extend APersistentVector which is:

public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
                                                               List,
                                                               RandomAccess, Comparable,
                                                               Serializable, IHashEq {

seancorfield16:10:30

Then PersistentVector extends that and implements a few extra interfaces:

public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce{

Jim Newton16:10:17

clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.PersistentVector))
#{:public}
clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.APersistentVector))
#{:public :abstract}
clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.IPersistentVector))
#{:interface :public :abstract}
clojure-rte.rte-core> 

Alex Miller (Clojure team)17:10:04

all of the A... types in clojure are generally base classes, I would guess most are abstract

Jim Newton19:10:19

and and I... classes are interfaces?

Alex Miller (Clojure team)19:10:52

yes, that's a common Java idiom

Alex Miller (Clojure team)19:10:03

(the A... is not, but it's common in the Clojure code base)

Alex Miller (Clojure team)19:10:33

which is not to say that it's totally followed - things like Countable are interfaces too

Jim Newton07:10:07

Can someone suggest a better name for this function? The function takes a class-name such as the symbol Number , uses resolve to get the class and then uses (comp :flags efl/type-reflect) to get a list of flags. Then returns a keyword from :abstract, :interface, :final, :public . My function has a really bad name at the moment. I need a better name for this.

(defn-memoized [class-type -class-type]
  "Takes a class-name and returns either :abstract, :interface, :public, or :final,
  or throws an ex-info exception."
  [t]
  (let [c (find-class t)
        r (refl/type-reflect c)
        flags (:flags r)]
    (cond
      (= c Object) ; case #1
      :abstract
      (contains? flags :interface) ; case #2
      :interface
      (contains? flags :final) ; case #3
      :final
      (contains? flags :abstract) ; case #4
      :abstract
      (= flags #{:public}) ; case #5
      :public
      
      :else
      (throw (ex-info (format "disjoint? type %s flags %s not yet implemented" t flags)
                      {:error-type :invalid-type-flags
                       :a-type t
                       :flags flags})))))
BTW, the function makes a special case of Object, when I'm still not sure is correct for my application. The more I think about it, the more I think case 1 and 5 should be merged into a single case. That might change in the future when I understand the situation better.

Jim Newton16:10:03

@seancorfield do you know of a subclass of clojure.lang.PersistentVector)

seancorfield17:10:09

Nothing within Clojure itself -- that's what I was just looking for in the Java source code. I'd be very surprised to find code even out in the wild that extends PersistentVector. A search on GitHub found code that extends APersistentVector and code that wraps PersistentVector (using it as an implementation detail inside some other "persistent" object).

Jim Newton16:10:18

such would be great for a test case of my code

Jim Newton16:10:54

I found an interesting one:

clojure-rte.rte-core> (:flags (refl/type-reflect BigInteger))
#{:public}
clojure-rte.rte-core> 

seancorfield17:10:09

Nothing within Clojure itself -- that's what I was just looking for in the Java source code. I'd be very surprised to find code even out in the wild that extends PersistentVector. A search on GitHub found code that extends APersistentVector and code that wraps PersistentVector (using it as an implementation detail inside some other "persistent" object).

seancorfield17:10:56

What's "interesting" about BigInteger @jimka.issy?

andy.fingerhut17:10:48

I found some code that I have used in the past to list most/all classes in a running JVM, but in trying to run it today I seem to have lost the recipe for what command line options when starting the JVM it requires to have the right privileges to work properly. It is some test code in this repository: https://github.com/jafingerhut/cljol

andy.fingerhut17:10:32

A tiny shell script that might work with some operating system / JDK versions, or did at one point, is in the file doc/generate-report.sh

andy.fingerhut17:10:51

it uses this library to do the actual finding of Java classses and I believe also interfaces: https://github.com/classgraph/classgraph

andy.fingerhut17:10:53

Here is a short sample use of calling the classgraph library from Clojure, with no other libraries required except classgraph and those included with Clojure: https://github.com/jafingerhut/cljol/blob/master/doc/sample-classgraph-use.clj