Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: support for nested classes. #1049

Closed
DartBot opened this issue Jun 19, 2012 · 57 comments
Closed

Feature request: support for nested classes. #1049

DartBot opened this issue Jun 19, 2012 · 57 comments
Labels
state-duplicate This issue or pull request already exists

Comments

@DartBot
Copy link

DartBot commented Jun 19, 2012

This issue was originally filed by zhygrr...@gmail.com


It would be nice if we can introduce classes local to a given enclosing class.

class Outer {
  void doSomething() {
    var inner = new Inner();
  }
  class _Inner {
       
  }
}

This allows define class that is invisible outside of the Outer class scope.

I understand that this feature requires more investigation. As for me it has a limited power if a class is not the first class object (or a class could be a class member like fields or methods).

@sethladd
Copy link

Removed Type-Defect label.
Added Type-Enhancement, Area-Language, Triaged labels.

@gbracha
Copy link

gbracha commented Jun 26, 2012

The idea of nested classes is very familiar to us (see. e.g., Beta, Newspeak). To avoid any misunderstandings, I should make it clear that we would NOT do this the way it was done in the Java programming language. Whether this will happen in Dart is an open question.


Set owner to @gbracha.
Added this to the Later milestone.
Added Accepted label.

@DartBot
Copy link
Author

DartBot commented Mar 26, 2014

This comment was originally written by astimo...@gmail.com


I would be very happy if inner classes were only a means of taking advantage of lexical scoping for the organization of classes by name.

class Foo
{
  Bar bar;
  Baz baz;

  Foo()
  {
    bar = new Bar();
    baz = new Baz();
  }

  class Bar
  {
  }

  class _Baz
  {
  }
}

new Foo.Bar(); // acceptable
new Foo._Baz(); // unacceptable

By "only a means of taking advantage of lexical scope" I mean to exclude the java behaviour of creating a runtime reference to the outer class unless the inner class is declared static. For that case the program can explicitly provide the outer class to the inner class.

Thus the following will not work:

class Foo
{
  Bar bar;

  Foo()
  {
    bar = new Bar();
    bar.speak();
  }

  void hello()
  {
    print("hello");
  }

  class Bar()
  {
    void speak()
    {
      hello();
    }
  }
}

But this will work:

class Foo
{
  Bar bar;

  Foo()
  {
    bar = new Bar(this);
    bar.speak();
  }
  
  void hello()
  {
    print("hello");
  }

  class Bar
  {
    final Foo foo;

    Bar(Foo foo)
    {
      this.foo = foo;
    }

    void speak()
    {
      foo.hello();
    }
  }
}

@kasperl
Copy link

kasperl commented Jul 10, 2014

Removed this from the Later milestone.
Added Oldschool-Milestone-Later label.

@kasperl
Copy link

kasperl commented Aug 4, 2014

Removed Oldschool-Milestone-Later label.

@DartBot
Copy link
Author

DartBot commented Sep 30, 2014

This comment was originally written by PortalProgra...@gmail.com


Any updates on this issue?

@gbracha
Copy link

gbracha commented Oct 6, 2014

Not really.

@MikeMitterer
Copy link

It quite often the case that an enum has a very strong relation to a class - because of this missing feature I can't write "inner-enums" - annoying.

@zoechi
Copy link

zoechi commented Mar 8, 2017

You can make the enum library-private. Not the same of course, but quite close.

@MikeMitterer
Copy link

Thanks - I know. But this:

class MyCoolClass {
    enum Props { HOT, NEW, COOL }
    ...
}
...
switch(props) {
    case MyCoolClass.Props.HOT:
    break;
    ...
}

is what I want.

I know - in Java its a bit problematic if the inner class is not static - C++ does it right. Dart should have a similar thing.

@zoechi
Copy link

zoechi commented Mar 9, 2017

I got it that you want that. The question is if it's worth to complicate the language for that.
What's the advantage of having it inside the class instead of outside, that makes it worth to make the language more complex?
I'm not arguing against it. Just curious what you think would be the big advantage.

@xster
Copy link

xster commented Mar 9, 2017

I think it's just for the communications/maintainability scalability of a systems engineering language.
Referring to say network.get(..., cache: Network.CachingStrategy.A) (which is a public API) is cleaner than having a NetworkCachingStrategy global class/enum lying around all the time.

@MichaelRFairhurst
Copy link

I would agree. One thing I would say as well, is that I think very rarely should dart files define multiple classes.

Usually when I do see it I don't disagree with the structuring of the code, but I do find it hard to use the resulting api.

import 'something.dart';

...
  var something = new Something();
  something.use(new UsedBySomething());
  new FinalizesSomethings.finalize(something);

There's no easy autocomplete for "what does the something package expose again?" Its also hard when reusing this code elsewhere, to figure out which import brings in which classes.

You could do prefixed imports, but now you've either got the tedious something.Something or you've got the cryptic smt.Something. Oh and you can no longer use smt or something as variables when you do this.

I think it would be a nice improvement if we only ever defined one class in a dart file, and relied on inner classes where they are so related that they deserve to be in the same file.

  var something = new Something();
  something.use(new Something.UsesThis());
  new Something.Finalizer(something)

You may also not be surprised to hear that I think this is less of an issue when the classes are named CommonFoo rather than FooCommon since that's basically what my new code is, just with an extra dot. But its pretty common that these things don't match up perfectly. And even then, you never know when "SomethingFinalizer" is in "something.dart" or "something_finalizer.dart".

Obviously there's a conflict here with the syntax, as Finalizer could be the name of a constructor on Something rather than a class on Something. Probably would be a ton of work to solve.

Overall my opinion is that it would be a small but appreciable win for dart.

@gdejohn
Copy link

gdejohn commented Mar 7, 2018

I should make it clear that we would NOT do this the way it was done in the Java programming language.

@gbracha In what way would you do it differently in Dart?

@eernstg
Copy link
Member

eernstg commented Mar 7, 2018

(PS: Gilad is not in the Dart team now, he went on to pursue new adventures elsewhere.)

That said, I think this is the right place to gather arguments for or against adding class nesting (static or otherwise) to Dart, but also that there is relatively modest support for the idea at this point.

@gdejohn
Copy link

gdejohn commented Mar 12, 2018

@eernstg Do you have any idea what Gilad meant about doing it differently than Java?

@MikeMitterer
Copy link

@eernstg Maybe like Kotlin - where an inner class is always static (Doesn't have a reference to it's outer class) except it is marked with the "inner" keyword
https://kotlinlang.org/docs/reference/nested-classes.html

@eernstg
Copy link
Member

eernstg commented Mar 14, 2018

Gilad's Newspeak includes nested classes, and they are related to the general nesting in BETA and gbeta (of patterns, which is a general concept that includes classes and a lot of other things in one language mechanism), and those languages all make nested classes members in a way that resembles methods (in particular, a nested class is subject to late binding). That's a rather powerful concept, aka virtual classes or virtual patterns, and — who knows — that might have been on his mind when he said ''we would NOT do this [like in Java]".

On the other hand, static inner classes are even less powerful than Java inner classes, they provide nothing else than scoping (that is, certain things will have shorter names in some contexts). So I doubt it was that.

In any case, virtual classes in Dart would be such a huge generalization (of many things, including the type system), so we won't get that in any reasonable amount of time. ;-)

@gusterwoei-veltra
Copy link

Yea I was trying to build a JSON to object mapping library but got stuck in this issue.

@phlebotinum
Copy link

I want Enums as sub-types of classes for more code clarity.

@SixSheeppp
Copy link

In java I always enclose the http request like this.
doGet(String requestUrl, DioRequestCallback callback) async { Response getResponse = await dioInstance.get(requestUrl); print(getResponse.statusCode); }
If the inner class not work,I might write the request in many places.And I cant custom to handle server response code in one place.

@SixSheeppp
Copy link

In java I always enclose the http request like this.
doGet(String requestUrl, DioRequestCallback callback) async { Response getResponse = await dioInstance.get(requestUrl); print(getResponse.statusCode); }
If the inner class not work,I might write the request in many places.And I cant custom to handle server response code in one place.

I find a way to resolve that.
doGet( {String requestUrl, successCallback(int statusCode), errorCallback(int statusCode)}) async { Response getResponse = await dioInstance.get(requestUrl); print(getResponse.statusCode); }
use function as param.

@mpowloka
Copy link

mpowloka commented May 17, 2019

Without it, there is no way for nesting necessary listeners inside the appropriate class and nesting Builder class in the builder design pattern. Any updates?

@wslaimin
Copy link

I want to use builder design pattern.Nested classes is necessary.

@pkyingyu
Copy link

Since everybody knows about it, I don't want to talk any more!

@jifferon
Copy link

Come on, it's been 8 years, goddamn

@eernstg
Copy link
Member

eernstg commented Oct 21, 2019

@egorikem wrote:

Come on, it's been 8 years, goddamn

I can understand that this is a serious source of impatience.

However, this feature never made it to the top of the list. It is my impression that the main reason is that it causes Dart software to become more complex, especially syntactically (because it could push Dart software in the direction of having a lot of huge, nested {...} blocks). Conversely, the case for having it has not been made in a sufficiently compelling manner.

In particular, it would be convenient that the names of certain entities can be abbreviated (e.g., calling a static method named m from inside a class C could use m() whereas any location outside C would have to use C.m()), so that could be an argument in favor of static nested classes or dynamic nested classes (like Java's inner classes). But static nested classes can be rewritten to top-level classes by using the class prefix C. as needed. Similarly, dynamic nested classes can be rewritten to top-level classes where a certain member, let's call it outer, is a reference to the (emulated) enclosing instance of C.

Access to an emulated nested class would be different (for clients using them as type annotations, or creating new instances of them). But having rewritten a given nested class to a top-level class, it is certainly possible to use that top-level class as a type annotation, or to create instances of it (passing a reference to the emulated enclosing object in a constructor), and I don't see how this would not be a complete and faithful emulation.

@mpowloka wrote:

Without it, there is no way for nesting necessary listeners
inside the appropriate class and nesting Builder class in
the builder design pattern

Maybe you could give an example, and then we could explore approaches where the nesting is emulated and see how inconvenient it is?

@cosinus84
Copy link

At least would be nice to have enum inside classes. Is very intuitive to have enums related in the same place.

@GensaGames
Copy link

GensaGames commented Nov 21, 2019

@eernstg

(static and inner) were implemented many years ago as a relatively straightforward program transformation, and we can certainly use the same techniques in Dart.

I don't understand why Java really should have context here. We can talk about nested classes inside every other language (React, Python, TS, basically most of them). And talking aside our subjective opinions above - there is no issue at all using them. For all other cases.

@GensaGames
Copy link

@eernstg ,

Maybe we can choose democratic way. Aside of your opinions we can create poll, where all contributors will vote, based on their experience. And finally resolve this question?

@eernstg
Copy link
Member

eernstg commented Dec 13, 2019

We can't promise a specific procedure, but an issue like this one with a lot of 👍's is taken seriously. At the same time, language changes do take time (e.g., dart-lang/sdk#22 is the big thing we're doing right now).

@easazade
Copy link

easazade commented Dec 23, 2019

this is the exact problem we faced as android developers back when i was using kotlin and android-studio instead of flutter. at first android team was working hard but then they got lazy and stuff which lead them to hack the problems instead of solving them. which lead to the creation of abominations like Android Support library which was a total hack. I guess i just have a fear of the same thing happening to flutter

@PandaGeek1024
Copy link

Any progress?

@eernstg
Copy link
Member

eernstg commented Jan 24, 2020

Everybody is still fully occupied working on non-nullable types (implementing it, porting the standard libraries, and lots of other stuff).

@camsteffen
Copy link

camsteffen commented Feb 18, 2020

I just want to add another case where nested classes are useful. Constants that are subdivided into categories.

For example:

int statusCode = HttpStatusCodes.ClientError.forbidden;

@tapizquent
Copy link

tapizquent commented Feb 27, 2020

Will this ever happen? I have a case where I have a TranslationLocalizations class and I would like to group my translation string inside this class by categories. Inner classes would help with this a lot

TranslationLocalizations.of(context).HomeScreen.title

@Douvi
Copy link

Douvi commented Apr 15, 2020

Dart is already in v2.8 and still cannot do any nested classes. Today, this feature is kind of basic as doing class extension...
Can you guys at least move it at the top of your list!

@arnold-parge
Copy link

If I'm not too late to the party, I have an approach(workaround) for inner/nested class:

class AppConstants {
  static const _Style Style = const _Style();
  static const _URL Url = const _URL();
}

class _Style {
  const _Style();
  final EdgeInsetsGeometry buttonPadding = const EdgeInsets.all(10.0);
}

class _URL {
  const _URL();
  final String game = 'https://example.com';
}

main() {
  var myStyleToUse = AppConstants.Style.buttonPadding;
}

I would really like the know the downsides of this though, if anyone can figure out. 🍻

@rockiedo
Copy link

I like your idea @arnold-parge. There're 2 downsides I can see at the moment:

  • You have to write more code 😅 .
  • It's violating the naming convention for constant identifiers, which should be following lowerCamelCase. You can find the convention here.

Happy coding. 🍻

@kevmoo
Copy link
Member

kevmoo commented Jun 25, 2020

@leafpetersen @munificent @lrhn – this should be a language issue?

@lrhn lrhn transferred this issue from dart-lang/sdk Jun 26, 2020
@lrhn
Copy link
Member

lrhn commented Jun 26, 2020

@kevmoo I've been refraining from moving all language issues in the SDK repository into the language repository. Obviously they belong there, but if there is no recent interest in the issue, then it might as well be left where it is.

@pedromassango
Copy link

@kevmoo I've been refraining from moving all language issues in the SDK repository into the language repository. Obviously they belong there, but if there is no recent interest in the issue, then it might as well be left where it is.

Can we expect this in Dart in the long term?

@lrhn
Copy link
Member

lrhn commented Jun 26, 2020

There are no current priorities that make us work on nested classes. The problems it solves are just not the most urgent problems for us to solve right now.
If priorities change, anything can happen.

... In the very long run everything is possible, and nothing is probable ...

@mateusfccp
Copy link
Contributor

I can't see even one case where this is useful.

@lrhn
Copy link
Member

lrhn commented Jun 28, 2020

#336 is the original language-repository issue for this request. Closing as duplicate of that.

@surajthomask
Copy link

Coming from Swift background and having spent couple of weeks in Dart and Flutter, I cannot express my disappointment on not having nested types. I sincerely hope you guys will work on this sooner, rather than later. 🙏

@bubnenkoff
Copy link

Am I right that next code is not valid and related with this issue?

main() {
  class M {}
}

error:

Error: 'class' can't be used as an identifier because it's a keyword.
Try renaming this to be an identifier that isn't a keyword.

@surajthomask
Copy link

@mateusfccp Here is an example on why nested types is such a needed feature; see the below swift code.

struct LoginState {

    private init() {}

    struct Loading {}

    struct Success {

        var userInfo: UserInfo?
    }

    struct Failure {

        var error: Error?
    }
}

let state = LoginState.Loading()
let anotherState = LoginState.Success(userInfo: UserInfo(name: "John", id: "us00012"))
let anotherStateAgain = LoginState.Failure(SomeError())

Having nested types allow us to reduce the clutter of having way too many types at global level. In the example above, all states related to Login are encapsulated inside the LoginState structure. In Dart, we will need to create LoginLoading, LoginSuccess, LoginFailure etc at the root level.

For large scale projects, lack of nested types does hurt the maintainability of the codebase.

@mateusfccp
Copy link
Contributor

mateusfccp commented Jan 24, 2021

@surajthomask Thanks for providing an example...

I still don't know what advantages it does bring... Is it only the namespace-like notation? Like LoginState.Loading? Or is there something else I am missing here?

In Dart I usually achieve the same by using factory constructors + private classes if I don't want them to be exported.

@AlexanderFarkas
Copy link

Is this feature on the radar? After finishing null safety?

@darkstarx
Copy link

darkstarx commented Mar 13, 2021

Unfortunately @arnold-parge 's workaround does not meet all the needs

class Base {
  final options = const _Options();
}

class _Options {
  const _Options();
  final option1 = 'option1';  // You can't declare this as compile-time constant
  final option2 = 'option2';
}

switch (option) {
  case Base.options.option1: break;  // << Here you can't use non const value Base.options.option1
  case Base.options.option2: break;
  ...
}

https://dart.dev/tools/diagnostic-messages#non_constant_case_expression

@Nikitae57
Copy link

Not having nested classes is a major drawback. Any updates?

@pinyin
Copy link

pinyin commented Mar 22, 2021

Inner class is essential when expressing some classes are only meaningful inside another class, in tree-like structures like UI.
This is especially true when one wants to write multiple visual components in one file. A lot of files in Flutter source would be much more cleaner if the author could have a better way to organize classes into small units inside one source file.

@om-ha
Copy link

om-ha commented Oct 19, 2021

I think this thread should be locked.

As @lrhn said:

#336 is the original language-repository issue for this request. Closing as duplicate of that.

On top of posting in the wrong place. Some of us are lashing negative reinforcements on Flutter community maintainers and Googlers.

Instead of phrasing our requests in a manner of negative reinforcements, just +1 the canonical issue (#336). If your motivation points for it is not mentioned, do mention them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state-duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests