PrologJ allows Prolog code to access the public methods and fields of Java objects and classes, and to create Java objects, using facilities based on Java reflection.
Two new built-in predicate are added to support calling Java code, and to allow recognizing Prolog terms that are Java objects, not standard Prolog terms.
@(Target, Method)@ is also defined as an infix operator to
facilitate its use, so the above example could be written as
Target @ Method. Target argument of this predicate must be one of the
followingclass/1, where the
argument is the fully qualified name of a class whose class method
or constructor is to be called.variable = either of the
above to call a method or constructor and bind the
variable to the return value of the method or constructor call.
(The mapping between Prolog and Java terms is based on the
standard mapping between types.)
Method argument of this predicates must be either an
atom or a compound term to specify the method/constructor to be called
and the arguments to call it with.new,
a constructor is called; otherwise, a class or instance method is
called.void).
false (return type boolean
or Boolean) or null of any reference
type. Methods whose return type
is any primitive type other than boolean are always
considered to succeed. (Constructors are considered to return a
non-null object of the class for which they are defined, though it
would usually not make sense to call a constructor and then not
save the object constructed!)
void).
| Conditions | Error Term |
|---|---|
Target is a variable |
instantiation_error |
Target is of the form class(C) or
Term = class(C), and C is a variable |
instantiation_error |
Target is of the form class(C) or
Term = class(C), and C is neither an atom
nor a variable |
type_error(atom, C) |
Target is of the form class(C) or
Term = class(C), and C names a class
that cannot be found |
system_error(class_not_found, C)
|
Target is of the form Term =
something and Term is not a variable. |
type_error(variable, Term) |
Method is a variable |
instantiation_error |
Method is neither an atom, nor a compound term,
nor an variable |
type_error(callable, Method) |
Method is a compound term, and one its arguments
is an erroneous parameter specifier |
The error that is listed for the specific violation below |
Method is the atom new or a compound
term whose functor is new and Target
is not of the form class(C) or
Term = class(C) |
type_error(class, Target) |
Method specifies a method and parameter types for
which no matching method is found in the class specified by
Target (either explicitly named or the class
of the object specified.) |
system_error(no_such_method, Method) |
| The execution of the method results in throwing an exception | If the exception is a Prolog error, then the error
thrown; else some sort of system_error with
Method as its last argument; the exact form
depends on the nature of the exception thrown |
Target is of the form Term = T,
and the return type of the method called is void |
system_error(unsupported_operation, Method) |
X = class('javax.swing.JOptionPane') @
'showInputDialog'(null('java.awt.Component'),
widen('java.lang.Object', 'What do you think?')),
write(X), nl.
Example: The following code creates a frame, puts a label into it,
and then packs and shows it.
F = class('javax.swing.JFrame') @ new,
L = class('javax.swing.JLabel') @ new('Hello World'),
P = F @ 'getContentPane',
P @ add(widen('java.awt.Component', L)),
F @ pack,
F @ show.
object(Term)var/1,
atom/1, integer/1, float/1,
and compound/1 would fail, and the term is neither
a stream nor a database connection.
One core predicate is extended to allow access to the fields of Java objects
arg(Field, Target, Value)arg/3 is extended to allow code written
in Prolog to access or alter public fields of a Java class or object.
A Prolog term can be unified with the current value of the field, converted
to an appropriate Prolog term, or an appropriate Prolog term can be converted
to a Java primitive or object type and stored in the field. arg/3 is distinguished from the
standard form of the predicate by the first argument. When accessing
a field of a class or object, the first (Field) argument is
an atom whose name is the name of the field, rather than an integer.Target argument must be eitherclass/1, where the
argument is the fully qualified name of a class whose static field
will be accessed.
Value argument must one of the following:set/1 where the argument
is a value to be stored into the field. This must be a Prolog
term of a type that can be converted to the declared type of
the field; or an uninstantiated variable, in which case
null is stored in the field. (However, an uninstantiated
variable is not allowed for a field that is of a primitive type,
because null cannot be stored in such a field.)
arg/3 predicate always succeeds if it executes without
error.arg/3 predicate succeeds
if and only if the unification is successful. Note:
null is considered equivalent to an uninstantiated
variable and unification is considered to succeed without any
instantiations being done.arg/3):| Conditions | Error Term |
|---|---|
Target is a variable |
instantiation_error |
Target is of the form class(C), and C
is a variable |
instantiation_error |
Target is of the form class(C), and C
is neither an atom nor a variable |
type_error(atom, C) |
Target is of the form class(C), and C
names a class that cannot be found |
system_error(class_not_found, C)
|
Target any compound term not of the form
class(C), or an atom or a number |
type_error(integer, Field) |
Field specifies a field for which no matching
public field can be found in the class specified by
Target (either explicitly named or the class
of the object specified.) |
system_error(no_such_field, Field) |
Field names an instance field, and
Target is of the form class(C) |
system_error(no_such_field, Field) |
Field names a static field, and
Target is not of the form class(C) |
system_error(no_such_field, Field) |
Value is of the form set(V), and
V is not compatible with the declared type
of the field specified by Field |
The error that is listed for the specific violation in the discussion of type conversion errors |
Value is of the form set(V),
and the field is final |
system_error(illegal_access, Field) |
| The Java reflection facility forbids access to the field for some other reason | system_error(illegal_access, Field) |
arg('MAX_VALUE', class('java.lang.Short'), M),
write(M), nl.
Example: Suppose that there exists a Java class with the following definition:
public class SetFieldDemo
{
public void println()
{
System.out.println(someField);
}
// Making a field public is not good practice in general -
// but is done here for illustration
public int someField;
}
Then the following Java code will result in the output shown below:
S = class('SetFieldDemo') @ new,
S @ println,
arg(someField, S, set(42)),
S @ println.
Output:
0
42
Because Java allows several methods/constructors to have the same name but different parameter type signatures, and because Java reflection looks up methods or constructors in a class based on the exact specification of parameter types, it is necessary to explicitly specify the types of the parameters of a call from PrologJ to a Java method or constructor. (The needed information is more than what can be inferred from the Prolog term types).
If the Method argument to the built-in predicate @/2
is a compound term, then its arguments are specifiers for the parameters
to be used in calling the Java method or constructor.
A parameter specifier must either be atomic, or it must a compound term of the form
null/1, widen/2, array/1 or
array/2, or it must be a compound term of arity 1
whose functor is one of the values listed below. It is an error for
a parameter specifier to be an uninstantiated variable.
String, if the specifier is an atom.
int, if the specifier is an integer.
double, if the specifier is a real number.
null/1 corresponds to a null
reference of a specific type, where the argument of the term is the
fully-qualified name of the class of the formal parameter of the Java
method/constructor.widen/2 corresponds to widening
an object to one of its superclasses or interfaces. The first argument
of widen/2 is an atom whose name is the fully-qualified name
of the class/interface as specified in the signature of the
method/constructor being called, and the second is either a constant of a
type that can be widened to the specified type, or a variable that has been
instantiated to the object to be widened, typically the return value of some
other method or constructor call or the value of some field.
array/1 or array/2
corresponds to an array.array/2, the first argument must be the fully-qualified
name of the class of the elements of the array. For primitive types,
the type name (e.g. boolean) can be used.
array/2, and the only argument of
array/1 must be a list, each of whose elements is a valid
parameter specifier in accordance with the rules given here (including
possibly array/1 or array/2 if the parameter
is an array of arrays). For array/1, the
type of the array is inferred from the type of the first parameter
specifier, and all of the parameter specifiers must be be assignment
compatible with it; for array/2, all of the parameter
specifiers must be assignment compatible with the element type
specified by the first argument.array/2, it is possible to specify an array of length 0
by using an empty list ([]); however, an empty list is
not legal with array/1 because there would be no way to
specify the type of the array elements. boolean'Boolean'char'Character'byte'Byte'short'Short'int'Integer'long'Long''BigInteger'float'Float'double'Double'String''PrologCompound''PrologList'| Conditions | Error Term |
|---|---|
| The parameter specifier is a variable | instantiation_error |
The parameter specifier is of the form null(C),
and C is a variable |
instantiation_error |
The parameter specifier is of the form null(C),
and C is neither an atom nor a variable |
type_error(atom, C) |
The parameter specifier is of the form null(C),
and C names a class that cannot be found |
system_error(class_not_found, C)
|
The parameter specifier is of the form null(C),
and C names a primitive class |
system_error(class_not_found, C) |
The parameter specifier is of the form widen(C, V),
and C is a variable |
instantiation_error |
The parameter specifier is of the form widen(C, V),
and C is neither an atom nor a variable |
type_error(atom, C) |
The parameter specifier is of the form widen(C, V),
and C names a class that cannot be found |
system_error(class_not_found, C)
|
The parameter specifier is of the form widen(C, V),
and C names a primitive class |
system_error(class_not_found, C)
|
The parameter specifier is of the form widen(C, V),
and V is a variable |
instantiation_error |
The parameter specifier is of the form widen(C, V),
and V does not represent a value that is assignable
to class C |
system_error(class_cast, V)
|
The parameter specifier is of the form array(V),
and V is a variable |
instantiation_error |
The parameter specifier is of the form array(V),
and V is the empty list |
domain_error(non_empty_list, V) |
The parameter specifier is of the form array(V),
and V is a partial list |
instantiation_error |
The parameter specifier is of the form array(V),
and V is an improper list |
type_error(list, V) |
The parameter specifier is of the form array(V),
and V is a list, and the first element of V represents
a value that is of a primitive type, and
one of the elements of V is of the form
null(C) |
system_error(illegal_argument, E)
|
The parameter specifier is of the form array(V),
and V is a list, and C is the class to which the
value specified by its first element belongs, and
one of its elements E does not represent a value
that is assignable to class C |
system_error(illegal_argument, E)
|
The parameter specifier is of the form array(C, V),
and C is a variable |
instantiation_error |
The parameter specifier is of the form array(C, V),
and C is neither an atom nor a variable |
type_error(atom, C) |
The parameter specifier is of the form array(C, V),
and C names a class that is not primitive and cannot be found |
system_error(class_not_found, C)
|
The parameter specifier is of the form array(C, V),
and V is a variable |
instantiation_error |
The parameter specifier is of the form array(C, V)
and V is a partial list |
instantiation_error |
The parameter specifier is of the form array(C, V)
and V is an improper list |
type_error(list, V) |
The parameter specifier is of the form array(C, V),
and V is not the empty list or a list |
type_error(list, V) |
The parameter specifier is of the form boolean(Value) or
'Boolean'(Value) and Value is a variable |
instantiation_error |
The parameter specifier is of the form boolean(Value) or
'Boolean'(Value) and Value is neither a variable
nor one of the atoms true, fail,
or false |
type_error(boolean, Value) |
The parameter specifier is of the form char(Value) or
'Character'(Value) and Value is a variable |
instantiation_error |
The parameter specifier is of the form char(Value) or
'Character'(Value) and Value is neither a variable
nor a one-character atom |
type_error(character, Value) |
The parameter specifier is of the form byte(Value) or
'Byte'(Value) or short(Value) or
'Short'(Value) or int(Value) or
'Integer'(Value) or long(Value) or
'Long'(Value) or 'BigInteger'(Value)
and Value is a variable |
instantiation_error |
The parameter specifier is of the form byte(Value) or
'Byte'(Value) or short(Value) or
'Short'(Value) or int(Value) or
'Integer'(Value) or long(Value) or
'Long'(Value) or 'BigInteger'(Value)
and Value is neither a variable nor an integer |
type_error(integer, Value) |
The parameter specifier is of the form float(Value) or
'Float'(Value) or double(Value) or
'Double'(Value) and Value is a variable |
instantiation_error |
The parameter specifier is of the form float(Value) or
'Float'(Value) or double(Value) or
'Double'(Value) and Value is neither a variable
nor a number |
type_error(number, Value) |
The parameter specifier is of the form 'String'(Value)
and Value is a variable |
instantiation_error |
The parameter specifier is of the form 'String'(Value)
and Value is a list which contains an element that
is a variable |
instantiation_error |
Method is a compound term, and one of its
arguments is of the form 'String'(Value)
and Value is a partial list |
instantiation_error |
The parameter specifier is of the form 'String'(Value)
and Value is an improper list |
type_error(list, Value) |
The parameter specifier is of the form 'String'(Value)
and Value is a list whose first element is an atom,
and the list contains an element E that is
not a one-character atom |
type_error(character, E) |
The parameter specifier is of the form 'String'(Value)
and Value is a list whose first element is an integer,
and the list contains an element E that is
not an integer |
type_error(integer, E) |
The parameter specifier is of the form 'String'(Value)
and Value is a list whose first element is an integer,
and the list contains an element E that is an
integer that is not a valid character code |
representation_error(character_code) |
The parameter specifier is of the form 'String'(Value)
and Value is a list whose first element E is neither
a variable, nor an atom, nor an integer |
type_error(integer, E) |
The parameter specifier is of the form 'String'(Value)
and Value is neither an a variable nor atomic nor a list |
type_error(atomic, Value) |
The parameter specifier is of the form 'PrologCompound'(Value)
and Value is a variable |
instantiation_error |
The parameter specifier is of the form 'PrologCompound'(Value)
and Value is neither a variable nor a compound term |
type_error(compound, Value) |
The parameter specifier is of the form 'PrologList'(Value)
and Value is a variable |
instantiation_error |
The parameter specifier is of the form 'PrologList'(Value)
and Value is neither a variable nor a list |
type_error(list, Value) |
| The parameter specifier P is a compound term that is not one of the acceptable forms for a parameter specifier | type_error(parameter, P) |
Example: Several of the options for parameter specifiers appear in the examples
for @/2 above.
Example: The following (admittedly contorted) example illustrates most of the options for parameter specifiers.
Suppose that there exists a Java class with the following definition:
public class ParameterListDemo
{
public static void demo(int n,
Object o1,
Object o2,
int [] intArray,
String [] stringArray,
Object [] objectArray1,
Object [] objectArray2)
{
System.out.println("n = " + n);
System.out.println("o1 = " + o1);
System.out.println("o2 = " + o2);
System.out.println("intArray length = " + intArray.length);
for (int i = 0; i < intArray.length; i ++)
System.out.println("intArray[" + i + "] = " + intArray[i]);
System.out.println("stringArray length = " + stringArray.length);
for (int i = 0; i < stringArray.length; i ++)
System.out.println("stringArray[" + i + "] = " + stringArray[i]);
System.out.println("objectArray1 length = " + objectArray1.length);
for (int i = 0; i < objectArray1.length; i ++)
System.out.println("objectArray1[" + i + "] = " + objectArray1[i]);
System.out.println("objectArray2 length = " + objectArray2.length);
for (int i = 0; i < objectArray2.length; i ++)
System.out.println("objectArray2[" + i + "] = " + objectArray2[i]);
}
}
Then the following Java code will result in the output shown below:
class('ParameterListDemo') @ demo(
42,
widen('java.lang.Object', hello),
null('java.lang.Object'),
array(long, [ 1, byte(2), short(3), int(4), long(5), long(6),
'Byte'(7), 'Short'(8), 'Integer'(9), 'Long'(10) ]),
array([ hello, world ]),
array([ widen('java.lang.Object', hello), world ]),
array('java.lang.Object', [])).
Output:
n = 42
o1 = hello
o2 = null
longArray length = 10
longArray[0] = 1
longArray[1] = 2
longArray[2] = 3
longArray[3] = 4
longArray[4] = 5
longArray[5] = 6
longArray[6] = 7
longArray[7] = 8
longArray[8] = 9
longArray[9] = 10
stringArray length = 2
stringArray[0] = hello
stringArray[1] = world
objectArray1 length = 2
objectArray1[0] = hello
objectArray1[1] = world
objectArray2 length = 0
The facilities described here are implemented by using the mechanisms
provided by Java reflection - specifically the getConstructor(),
getField(), and getMethod() methods of class
Class and the various facilities provided in package
java.lang.reflect. The use of these mechanisms creates
certain requirements for the implementation of the facilities for accessing
Java from PrologJ:
String must be referred to by the atom 'java.lang.String'
. (Where primitive class names are allowed, the standard name of
the primitive class can be used - e.g. int).
String parameter
('What do you think?') to class Object
because the showInputDialog method of class
JOptionPane declares its second parameter as being of class
Object. However, it was not necessary to widen
'Hello World' in the second example, because the constructor of
JLabel is declared to take a String parameter.