-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Scope of Change
The lang.types.ArrayList class and the relevant classes from util.collections
will be changed to support array access (read/write) and foreach() iteration.
The classes from the collections API will furthermore support type boxing
and provide generic variants of themselves. The text.String class will be
deprecated and replaced by lang.types.String and lang.types.Character.
Rationale
Easier / intuitive interface to "array" types.
Functionality
Class overview
* lang.types.ArrayList is an immutable, zero-indexed list of values of any
type and most closely resembles an array in Java.
* util.collections.Vector is a resizable array of objects.
* util.collections.HashTable is a map where both keys and values are
objects.
Array access
The ArrayAccess interface requires implementing the following methods:
offsetGet(), offsetSet(), offsetExists() and offsetUnset(). These
overload getting ($value= $object[$key]), setting ($object[$key]= $value)),
testing (isset($object[$key])) and removing (unset($object[$key])).
<?php
with ($a= new ArrayList(1, 2, 3)); {
// Element access
$one= $a[0]; // $one contains "1"
$a[1]+= 1; // The second list entry (2) will be increased
$a[2]= 4; // The third list entry (3) will be set to 4
// ArrayList is immutable in size!
$a[-1]= 1; // Throws an IndexOutOfBoundsException
$a[3]= 1; // - " -
$err= $a[4]; // - " -
$a[]= 1; // - " -
unset($a[0]); // Throws an IllegalArgumentException
// Keys are integers only!
$a['foo']= 1; // Throws an IllegalArgumentException
$f= $a['foo']; // - " -
// Testing
isset($a[0]); // TRUE
isset($a[-1]); // FALSE
isset($a[3]); // FALSE
}
with ($v= new Vector()); {
// The add() method and its overloaded equivalent
$v->add(new String('hello'));
$v[]= new String('hello');
// The get() method and its overloaded equivalent
$hello= $v->get(0);
$hello= $v[0];
// The set() method and its overloaded equivalent
$v->set(0, new String('world'));
$v[0]= new String('world');
// The remove() method and its overloaded equivalent
$v->remove(0);
unset($v[0]);
}
with ($h= new HashTable()); {
// The put() method and its overloaded equivalent
$h->put(new String('stage-url'), new URL('xp://localhost:6448'));
$h[new String('stage-url')]= new URL('xp://localhost:6448');
// Primitives in keys are "boxed"
$h->put('stage-url', new URL('xp://localhost:6448'));
$h['stage-url']= new URL('xp://localhost:6448');
// The get() method and its overloaded equivalent
$url= $h->get(new String('stage-url'));
$url= $h[new String('stage-url')];
// Primitives in keys are "boxed"
$url= $h->get('stage-url');
$url= $h['stage-url'];
}
with ($s= new HashSet()); {
// The add() method and its overloaded equivalent
$s->add(new String('X'));
$s[]= new String('X');
// The contains() method and its overloaded equivalent
$s->contains(new String('X'));
isset($s[new String('X')]);
// The remove() method and its overloaded equivalent
$s->remove(new String('X'));
unset($s[new String('X')]);
}
?>Iteration
The IteratorAggregate interface requires implementing a method called
getIterator() which must return an Iterator instance. This makes a class
usable inside the foreach statement.
<?php
// ArrayList foreach-iteration, prints "1 2 3 "
foreach (new ArrayList(1, 2, 3) as $value) {
echo $value, ' ';
}
// Vector foreach-iteration, prints "1 2 "
foreach (new Vector(array(new String('1'), new String('2'))) as $value) {
echo $value, ' ';
}
// HashSet foreach-iteration, prints "1 2 "
$s= new HashSet();
$s->addAll(array(new String('1'), new String('2');
foreach ($s as $value) {
echo $value, ' ';
}
// HashTable foreach-iteration, prints "development production "
$h= new HashTable();
$h['development']= new URL('xp://localhost:6448');
$h['production']= new URL('xp://jboss.example.com:6448');
foreach ($h->keys() as $key) {
echo $key, ' ';
}
?>Generics
To be able to create generics with the "new" keyword, we would need to
extend PHP's syntax or change constructor signatures. Both of those have
obvious downsides.
Therefore, a new core function "create" is introduced. Here's its prototype:
lang.Generic create(mixed arg) throws IllegalArgumentException
To create a generic object, the argument passed to create() must be a string
of the form ClassType "<" ComponentType [ ", " ComponentType ] ">". Class
names referenced by ClassType and ComponentType may be qualified or not and
need to refer to loaded XP classes or interfaces.
Examples:
<?php
with ($hash= create('HashTable<String, String>')); {
$hash['key']= new String('value');
$hash['key']= new Integer(1); // Throws an IllegalArgumentException
$hash[1]= new String('value'); // - " -
$value= $hash['key'];
$value= $hash[1]; // Throws an IllegalArgumentException
}
with ($func= create('util.collections.HashTable<String, lang.Runnable>')); {
$func['hello']= newinstance('lang.Runnable', array(), '{
public function run() {
Console::writeLine("Hello");
}
}');
$func['hello']->run();
}
with ($vector= create('Vector<lang.types.Number>')); {
$vector[]= new Integer(1);
$vector[]= new Float(1.0);
$vector[]= new Long(6100);
}
with ($routines= create('HashSet<lang.XPClass>')); {
$routines[]= XPClass::forName('lang.reflect.Method');
$routines[]= XPClass::forName('lang.reflect.Constructor');
}
?>Supporting generics in classes
To add generics support to a class, a public member named "__generic" must
be introduced. In all methods, the generic types must be verified manually.
Example:
<?php
class SortedList extends Object {
public
$__generic = array();
public function add($element) {
if ($this->__generic && !$element instanceof $this->__generic[0]) {
throw new IllegalArgumentException(
'Element '.xp::stringOf($element).' must be of '.$this->__generic[0]
);
}
// ...
}
}
?>This poses a higher burden when programming (compared to one being able to
express this syntactically) but does not introduce a great performance
penalty like http://experiments.xp-framework.net/?people,friebe,generics
does (transforms syntactically defined generic declarations to the notation
seen above when a class is loaded).
Additional note: create() overloaded
The create() functionality provides an overloaded version of itself:
When passed a lang.Generic instance, the instance is simply returned.
This serves the purpose of chaining after "new", which is not supported
by the PHP language:
<?php
echo new Date()->toString(); // Parse error
?>The following is the workaround:
<?php
echo create(new Date())->toString();
?>Additional note: ArrayList vs. Vector
The distinction between those two is that ArrayList is a thin, immutable
wrapper type without utility functions such as indexOf() or contains(),
whereas the Vector class provides a full "list" API.
Additional note: ArrayList constructor
lang.types.ArrayList's constructor will be changed to accept varargs
instead of an array. The classes that use lang.types.ArrayList will
be adapted to this change.
To be able to create an ArrayList from an array or from a known size,
a new method "newInstance" will be added. Usage as follows:
<?php
// Constructing an arraylist from an array
$a= ArrayList::newInstance(array(1, 2, 3, 4));
$four= $a->length;
// Constructing an empty arraylist
$a= ArrayList::newInstance(4);
$four= $a->length;
?>Security considerations
n/a
Speed impact
Slightly slower (more methods, extra implementation checks)
Dependencies
- A new interface util.collections.IList will be introduced. An
implementation will exists in form of the new class
util.collections.Vector. - xp-framework/rfc Add lang.types.String #101 - The lang.types.String class will be needed for
boxing to work.
Related documents
- http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html
Zend-Engine internal interface ArrayAccess - http://www.php.net/~helly/php/ext/spl/interfaceIteratorAggregate.html
Zend-Engine internal interface IteratorAggregate - http://xp-framework.net/rfc/contrib/rfc0106-remote.diff
Patch for remote API (apply in xp/trunk) - http://experiments.xp-framework.net/?arena,arrayaccess
Experiment and unittests - http://java.sun.com/docs/books/tutorial/java/generics/
Generics lesson - http://msdn2.microsoft.com/en-us/library/ms379564(VS.80).aspx
An Introduction to C# Generics