41

I want to create a more specialized list in dart. I can't directly extend List. What are my options?

Seth Ladd
  • 112,095
  • 66
  • 196
  • 279

6 Answers6

42

To make a class implement List there are several ways :

import 'dart:collection';

class MyCustomList<E> extends ListBase<E> {
  final List<E> l = [];
  MyCustomList();

  void set length(int newLength) { l.length = newLength; }
  int get length => l.length;
  E operator [](int index) => l[index];
  void operator []=(int index, E value) { l[index] = value; }

  // your custom methods
}
import 'dart:collection';

class MyCustomList<E> extends Base with ListMixin<E> {
  final List<E> l = [];
  MyCustomList();

  void set length(int newLength) { l.length = newLength; }
  int get length => l.length;
  E operator [](int index) => l[index];
  void operator []=(int index, E value) { l[index] = value; }

  // your custom methods
}
import 'package:quiver/collection.dart';

class MyCustomList<E> extends DelegatingList<E> {
  final List<E> _l = [];

  List<E> get delegate => _l;

  // your custom methods
}
import 'package:collection/wrappers.dart';

class MyCustomList<E> extends DelegatingList<E> {
  final List<E> _l;

  MyCustomList() : this._(<E>[]);
  MyCustomList._(l) :
    _l = l,
    super(l);

  // your custom methods
}

Depending on your code each of those options has their advantages. If you wrap/delegate an existing list you should use the last option. Otherwise, use one of the two first options depending on your type hierarchy (mixin allowing to extend another Object).

ThinkDigital
  • 3,189
  • 4
  • 26
  • 34
Alexandre Ardhuin
  • 71,959
  • 15
  • 151
  • 132
  • 1
    You can also use DelegatingList from package:collection/wrappers.dart in a similar way to the one from quiver. – lrn Feb 20 '14 at 09:24
  • I'm trying the second option, but getting an error the IDE under the `Base` telling `Minin can apply only for classes | undefined class Base`! – Hasan A Yousef Mar 03 '18 at 12:27
  • Using the first approach, how to define list of lists with this approach, I could not define `MyCustomList [MyCustomList]' :( – Hasan A Yousef Mar 03 '18 at 14:35
  • Another point, is there a way to convert my `List` to `MyCustomList`, something I can develop like `.MyCustomList` or so! – Hasan A Yousef Mar 03 '18 at 15:34
  • 1
    What is the best way to do this if I essentially just need a 'typedef', e.g. define class Grid to get rid of the List> definition. Is it possible to skip redefining the getters and setters and delegate to super? – relet May 03 '19 at 03:42
  • `.add()` does not work with ListBase.. There is a comment in the mixin saying it does not, and I just tried your code in dartpad and indeed add does not work – Ced Jun 23 '22 at 11:59
24

There is a ListBase class in dart:collection. If you extend this class, you only need to implement:

  • get length
  • set length
  • []=
  • []

Here is an example:

import 'dart:collection';

class FancyList<E> extends ListBase<E> {
  List innerList = new List();

  int get length => innerList.length;

  void set length(int length) {
    innerList.length = length;
  }

  void operator[]=(int index, E value) {
    innerList[index] = value;
  }

  E operator [](int index) => innerList[index];

  // Though not strictly necessary, for performance reasons
  // you should implement add and addAll.

  void add(E value) => innerList.add(value);

  void addAll(Iterable<E> all) => innerList.addAll(all);
}

void main() {
  var list = new FancyList();

  list.addAll([1,2,3]);

  print(list.length);
}
Seth Ladd
  • 112,095
  • 66
  • 196
  • 279
21

A new way of extending classes was introduced with Dart 2.6.
You can now create an extension of List like this:

extension MyCustomList<T> on List<T> {
  // Any methods you want can be added here.
}

The methods you add can be used implicitly, i.e. you can just use them on any List when you have your extension imported.
Here is an example from the feature specification:

extension MyFancyList<T> on List<T> {
  int get doubleLength => this.length * 2;
  List<T> operator-() => this.reversed.toList();
  List<List<T>> split(int at) => 
      <List<T>>[this.sublist(0, at), this.sublist(at)];
  List<T> mapToList<R>(R Function(T) convert) => this.map(convert).toList();
}

You can use these new members on any List, e.g. like this:

const list = <String>['some', 'elements'];

list.doubleLength; // Evaluates to 4.
creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402
7

The answers to this are pretty outdated, and I'm in the process of doing this for my own project, so I thought I'd help some people out by posting a really clean answer that doesn't involve any overriding or implementing of things.

The quiver package has an extendable List class called DelegatingList that makes extending a list trivial.

class FruitList extends DelegatingList<Fruit> {
    final List<Fruit> _fruits = [];

    List<Fruit> get delegate => _fruits;

    // custom methods
}

Hopefully this helps someone who comes across this question like I did!

Colin
  • 71
  • 1
  • 1
  • This worked for me, however `DelegatingList` also appears to be available in `package:collection` which I already had in my project, so I didn't need the `quiver` dependency. – Mrb83 May 11 '23 at 08:49
2

Following on from the answer above, you can create an immutable list like this:

class ImmutableList<E> extends ListBase<E> {
  late final List<E> innerList;

  ImmutableList(Iterable<E> items) {
    innerList = List<E>.unmodifiable(items);
  }

  @override
  int get length => innerList.length;

  @override
  set length(int length) {
    innerList.length = length;
  }

  @override
  void operator []=(int index, E value) {
    innerList[index] = value;
  }

  @override
  E operator [](int index) => innerList[index];
}
Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
-2
    //list is your given List and iterable is any object in dart that can be iterated
    list.addAll(Iterable)
Aditya Joardar
  • 580
  • 5
  • 11