Inheritance of static variables and methods

3,296 views
Skip to first unread message

maco young

unread,
Nov 27, 2013, 11:00:36 PM11/27/13
to mi...@dartlang.org
from https://www.dartlang.org/docs/spec/latest/dart-language-specification.html#h.i641x57pmqjt (see stackoverflow)

Inheritance of static methods has little utility in Dart. Static methods cannot be overridden. Any required static function can be obtained from its declaring library, and there is no need to bring it into scope via inheritance. Experience shows that developers are confused by the idea of inherited methods that are not instance methods.

I see the reasoning, but strongly disagree:

philosophically, if you're exposing the super class in the extended class, one should expose the super class.  period.

pragmatically, under normal circumstances it may be considered unnecessary - just look at the code, duh!  but when working with classes generically (using mirrors), non-inheritance is a real pain for many reasons.  here are three:

1. you don't necessarily know where the method may have originated - this require traversing up through supers to find the method before it can be called.

2. even if you know the class originating the method, you need to build a reflect-type edifice to actually run it

3. in generic classes, static variables are wonderful for holding meta info, from a practical standpoint making this type of code much easier to read and maintain.

Jos Hirth

unread,
Nov 27, 2013, 11:10:33 PM11/27/13
to mi...@dartlang.org
As far as I know, Dart's behavior is analogous to Java's or C#'s. You can't override them (and you can't specify them in interfaces).

In Dart, you can't even access static stuff via instances. So, even if you could do some of those things, it wouldn't be of much use, I guess.

Peter Ahé

unread,
Nov 28, 2013, 5:40:13 AM11/28/13
to General Dart Discussion
Dart's behavior regarding static methods is nothing like Java:

In Java, you can call a static method through an instance. The called
method is statically bound, not dynamically, and it is a common source
of confusion. My advice to Java programmers: don't do it and use tools
that help you avoid it. AFAIK, this unfortunate behavior was due to a
bug in an early version of javac and was promoted to a feature to
preserve compatibility.

In Java, you can "override" a static method. However, as I mentioned
in the previous paragraph, when invoked through an instance, there is
no dynamic dispatch. This is a common source of confusion, and you
should avoid it. Some people call this fauxveride.

Cheers,
Peter
> --
> For other discussions, see https://groups.google.com/a/dartlang.org/
>
> For HOWTO questions, visit http://stackoverflow.com/tags/dart
>
> To file a bug report or feature request, go to http://www.dartbug.com/new
>
> To unsubscribe from this group and stop receiving emails from it, send an
> email to misc+uns...@dartlang.org.

maco young

unread,
Nov 28, 2013, 8:16:33 PM11/28/13
to mi...@dartlang.org
seems there are two issues
1. whether a static method is presented as an instance method
2. whether a static method is inherited

in regard to #1 you're saying it originally included in Java as a bug and became a feature and should be dropped in Dart.  that's cool.  that makes sense.  (personally not at all a fan-boy of Java.)

but in regard to #2, whether a static method is inherited, is a horse of a different color.  even if compile time, seems to me there is no reason a static method/variable should not be inherited.

have found that, in Dart, when dealing with classes generically through mirrors, static variables are wonderful - there's really no other good way to pass meta information about a class.

Greg Lowe

unread,
Nov 28, 2013, 10:28:30 PM11/28/13
to mi...@dartlang.org
 there's really no other good way to pass meta information about a class.

Alex Tatumizer

unread,
Nov 28, 2013, 11:24:50 PM11/28/13
to mi...@dartlang.org
I think the reason for limitation comes from the following reasoning:
if static methods are inherited, they can be overridden (by general rule: whatever is inherited, can be overridden, too).
If they can be overridden, are they accessible polymorphically? Both answers YES and NO are bad.
To illustrate, under current definition of the language, the following program will print 1.
Under definition you propose, it will print what? It should print 2, right? Whatever it prints, someone will say it's unexpected. 
class A {
  static foo() => 1;
  bar() => print(foo());
}
class B extends A {
  static foo() =>2;
}
main() {
  new B().bar();
}

Peter Ahé

unread,
Nov 29, 2013, 3:44:54 AM11/29/13
to General Dart Discussion
On Fri, Nov 29, 2013 at 2:16 AM, maco young <bangk...@gmail.com> wrote:
> but in regard to #2, whether a static method is inherited, is a horse of a
> different color. even if compile time, seems to me there is no reason a
> static method/variable should not be inherited.

I see no reason that static members should be inherited. And I think
inheriting static members leads to confusion.

Cheers,
Peter

cc "maco" young

unread,
Nov 29, 2013, 1:50:51 AM11/29/13
to mi...@dartlang.org
Greg - that worked fine.  thanks for the lead

now no longer have any vested interest in changing the behavior of static methods


cc "maco" young

unread,
Nov 29, 2013, 1:25:58 AM11/29/13
to mi...@dartlang.org
yes, your reasoning is correct.


john smith

unread,
Nov 29, 2013, 5:09:57 AM11/29/13
to mi...@dartlang.org
>> 1. you don't necessarily know where the method may have originated - this require traversing up through supers to find the method before it can be called.

I my new project I will try to use the following solution.

import 'package:queries/reflection.dart';

void main() {
  print(Reflection.typeGetQualifiedName(A));
  var a = Reflection.typeGetVariable(A, #a, flags:  BindingFlags.PUBLIC | BindingFlags.STATIC);
  if(a != null) {
    print("${Reflection.typeGetQualifiedName(A)} have 'a'");
  }

  print(Reflection.typeGetQualifiedName(B));
  a = Reflection.typeGetAccessor(B, #a, flags: BindingFlags.STATIC);
  if(a == null) {
    print("${Reflection.typeGetQualifiedName(B)} does not have 'a'");
  }
}

class A {
  static int a;
}

class B extends A {
}  

And some code from package.

class Reflection {
  static final mirrorSystem = currentMirrorSystem();

  static String symbolToString(Symbol symbol) {
    return MirrorSystem.getName(symbol);
  }

  static DeclarationMirror typeGetAccessor(Type type, Symbol name, {int flags : BindingFlags.ALL, bool inherited : true}) {
    if(name == null) {
      throw new ArgumentError("name: $name");
    }

    return typeGetAccessors(type, flags : flags, inherited : inherited)[name];
  }

  static Map<Symbol, DeclarationMirror> typeGetAccessors(Type type, {int flags : BindingFlags.ALL, bool inherited : true}) {
    if(flags == null) {
      throw new ArgumentError("bindingFlags: $flags");
    }

    return typeGetMembers(type, flags : flags, inherited : inherited, members : MemberTypes.ACCESSOR);
  }

  static DeclarationMirror typeGetContructor(Type type, Symbol name, {int flags : BindingFlags.ALL, bool inherited : true}) {
    if(name == null) {
      throw new ArgumentError("name: $name");
    }

    return typeGetContructors(type, flags : flags, inherited : inherited)[name];
  }

  static Map<Symbol, DeclarationMirror> typeGetContructors(Type type, {int flags : BindingFlags.ALL, bool inherited : true}) {
    return typeGetMembers(type, flags: flags, inherited : inherited, members: MemberTypes.CONSTRUCTOR);
  }

  static DeclarationMirror typeGetMember(Type type, Symbol name, {int flags : BindingFlags.ALL, bool inherited : true, int types : MemberTypes.ALL}) {
    if(name == null) {
      throw new ArgumentError("name: $name");
    }

    return typeGetMembers(type, flags: flags, inherited : inherited, members: types)[name];
  }

  static Map<Symbol, DeclarationMirror> typeGetMembers(Type type, {int flags : BindingFlags.ALL, bool inherited : true, int members : MemberTypes.ALL}) {
    if(flags == null) {
      throw new ArgumentError("flags: $flags");
    }

    if(inherited == null) {
      throw new ArgumentError("inherited: $inherited");
    }

    if(members == null) {
      throw new ArgumentError("memberTypes: $members");
    }

    var classMirror = typeGetTypeMirror(type);
    var declarations = new Map<Symbol, DeclarationMirror>();
    if(classMirror is ClassMirror) {
      var classes = new List<ClassMirror>();
      if(inherited) {
        while(true) {
          classes.add(classMirror);
          classMirror = classMirror.superclass;
          if(classMirror == null) {
            break;
          }
        }
      } else {
        classes.add(classMirror);
      }

      var library = classes[0].owner;
      for(var level = classes.length - 1; level >= 0; level--) {
        var classMirror = classes[level];
        for(DeclarationMirror declaration in classMirror.declarations.values) {
          var flag = 0;
          var member = 0;
          var owned = false;
          if(declaration.owner != null && declaration.owner.owner == library) {
            owned = true;
          }

          if(declaration.isPrivate) {
            if(owned) {
              flag |= BindingFlags.PRIVATE;
            }
          } else {
            flag |= BindingFlags.PUBLIC;
          }

          if(!declaration.isPrivate || owned) {
            if(flags == BindingFlags.ALL && members == MemberTypes.ALL) {
              declarations[declaration.simpleName] = declaration;
              continue;
            }

            if(declaration is VariableMirror) {
              if(declaration.isStatic) {
                if(level == 0) {
                  flag |= BindingFlags.STATIC;
                } else {
                  continue;
                }

              } else {
                flag |= BindingFlags.INSTANCE;
              }

              member = MemberTypes.VARIABLE;
            } else if(declaration is MethodMirror) {
              if(declaration.isGetter || declaration.isSetter) {
                if(declaration.isStatic) {
                  if(level == 0) {
                    flag |= BindingFlags.STATIC;
                  } else {
                    continue;
                  }

                } else {
                  flag |= BindingFlags.INSTANCE;
                }

                member = MemberTypes.ACCESSOR;
              } else if(declaration.isConstructor && level == 0) {
                member = MemberTypes.CONSTRUCTOR;
              } else {
                member = MemberTypes.METHOD;
              }
            } else {
              continue;
            }

            if((flags & flag) != 0 && (members & member) != 0) {
              declarations[declaration.simpleName] = declaration;
            }
          }
        }
      }
    }

    return declarations;
  }

  static Symbol typeGetQualifiedName(Type type) {
    return typeGetTypeMirror(type).qualifiedName;
  }

  static DeclarationMirror typeGetMethod(Type type, Symbol name, {int flags : BindingFlags.ALL, bool inherited : true}) {
    if(name == null) {
      throw new ArgumentError("name: $name");
    }

    return typeGetMethods(type, flags : flags, inherited : inherited)[name];
  }

  static Map<Symbol, DeclarationMirror> typeGetMethods(Type type, {int flags : BindingFlags.ALL, bool inherited : true}) {
    return typeGetMembers(type, flags: flags, inherited : inherited, members: MemberTypes.METHOD);
  }

  static Symbol typeGetSimpleName(Type type) {
    return typeGetTypeMirror(type).simpleName;
  }

  static TypeMirror typeGetTypeMirror(Type type) {
    if(type == null) {
      return mirrorSystem.voidType;
    } else if(type == dynamic) {
      return mirrorSystem.dynamicType;
    }

    return reflectType(type);
  }

  static DeclarationMirror typeGetVariable(Type type, Symbol name, {int flags : BindingFlags.ALL, bool inherited : true}) {
    if(name == null) {
      throw new ArgumentError("name: $name");
    }

    return typeGetVariables(type, flags : flags, inherited : inherited)[name];
  }

  static Map<Symbol, DeclarationMirror> typeGetVariables(Type type, {int flags : BindingFlags.ALL, bool inherited : true}) {
    return typeGetMembers(type, flags: flags, inherited : inherited, members: MemberTypes.VARIABLE);
  }

  static Type typeMirrorGetType(TypeMirror typeMirror) {
    if(typeMirror == null) {
      return null;
    }

    if(typeMirror is ClassMirror) {
      if(typeMirror.hasReflectedType) {
        return typeMirror.reflectedType;
      }
    }

    if(typeMirror == mirrorSystem.dynamicType) {
      return dynamic;
    }

    return null;
  }
}

Reply all
Reply to author
Forward
0 new messages