COMMUNITY

How to access methods in elements of Array<T> inside a class implementing a generic interface<T>


(Kyliathy) #1

I have a class CollectionParentBase that implements an Interface CollectionParentI<T> and which defines a toString() method. I then have an array of objects of type T, all of which have CollectionParentBase as an ancestor. In the toString() of CollectionParentBase I try to go through all children and concatenate the result of their toString(). However, that doesn’t work. The compiler says that CollectionParentBase.T has no field toString.

Here’s all the code involved.

class CollectionParentBase<T, U> extends CollectionBase implements CollectionParentI<T, U>
{

	public var parent: T;

	@:isVar public var children (get, null): Array<U>;
	/**
	* Returns the children of this CollectionBase.
  **/
	public function get_children (): Array<U>
	{
		if (children == null) children = new Array<U>();
		return children;
	}

	private function new (name: String)
	{
		super(name);
	}

	public function toString(): String
	{
		var ret: String = "";
		for (child in children)
		{
			//var childC: CollectionParentI<U> = cast(child, CollectionParentI<U>);
			//The above cast fails saying that the argument should be dynamic.
			//If I try to remove the <U>, then it fails, of course, saying that there's insufficient type parameters.
			ret += child.toString(); //no field toString
		}
		return ret;
	}

}

interface CollectionParentI<T, U>
{

	public var parent (default, default): T;

	public var children (get, null): Array<U>;

	public function toString(): String;

}

I tried using @:generic but I can’t seem to get my head around how all that works. I think the manual could use more examples, or links to the Haxe Code Cookbook (which in turn could use more examples :slight_smile: ). Which brings me to…


(Jens Fischer) #2

Why the need for the explicit toString() call anyway? That should happen automatically when concatenating strings. Or does that also not work for you in this case?

Btw, minor code style thing: set_ and get_ methods are usually declared private, since you normally don’t want to call them directly / expose them as the public API of something (a private setter doesn’t mean that set access to the property is private).


(Kyliathy) #3

Thanks! :). Regarding toString(), for some reason I tried to do that with an object some time ago and it didn’t work. It was probably because I didn’t override it in the class, because now, once I removed the toString now, it worked :smiley:

I’m still curious about the answer: how could I achieve calling a function in U, as a member of Array<U>? All objects in that children Array are inheriting from CollectionParentBase, which in turn implements the Interface defining toString. What am I doing wrong?

Maybe I’m missing some basic stuff due to the thousand things swimming in my head and/or the hectic program I have today and/or being tired after several 6-hours nights (thanks to the joy of Haxing :smiley: ) . Or I’m missing some advanced stuff due to being rusty :slight_smile:


(Kyliathy) #4

I simplified the code somewhat, bundling the two interfaces into the same one. Also, I actually realized that I do need to create a custom toString method which recursively iterates through a hierarchy of classes. The CollectionParentI<T, U> interface allows me to chain classes in a hierarchy, using parents which own arrays of children. So, in the base class CollectionParentBase I want to create a toString that recurses through all children. And I can’t do that if I can’t type the elements. I even tried the code below, trying to provide a parent as an argument of type T, but I can’t get any code completion or compiling to work.

	public function toStringFullDump(parent: T = null): String
	{
		parent.toString(); //Error:(42, 3) Null<CollectionParentBase.T> has no field toString
	}

(Kyliathy) #5

Anybody? :slight_smile:


(Mike Robinson) #6

I think that … = null … is probably wrong in the above code-snippet. (Does it even make sense for this to be “nullable?”)

I think that I would be using interfaces here. You know that everything in your collection needs to implement an interface that provides toString() capability.

When you use things like <T> you are by design saying nothing about what the type can actually be. But in this case you do have a requirement: “an object that does not implement toString() must not be here.” Therefore, you need to say that to Haxe using interfaces and types, so that it can properly detect at compile time when you attempt to add something to a collection which the associated code won’t be able to use.

A fully-dynamic language like JavaScript can figure out these things at runtime (usually just spitting out a silent error-message if they’re not there), but Haxe doesn’t work that way. Haxe can catch your mistake and keep it from happening in the first place.


(Eric Bishton) #7

As far as getting code completion to work in IntelliJ IDEA (assuming you’re still using that), there is an option to use the compiler’s completion functionality. You can find it in the menu File -> Project Structure -> SDKs (left panel) -> (middle panel) -> Use Compiler (checkbox). This uses the compiler for completions in addition to the Haxe plugin’s internal completion engine (which doesn’t know as much about typing and knows nothing about macros).