Win a copy of Head First Agile this week in the Agile forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Why does final class cause "exporting non-public" warning?  RSS feed

 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This code generates no warnings:



However, making Outer final provokes an "Exporting non-public type through public API" warning at Line 3:



I can't see why making Outer final has this effect. Can anyone tell me?
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Might it be because, even though Inner is protected, while Outer is public and not final, it can be extended, making Inner accessible to Outer's subclasses? In that sense, Inner is still accessible outside Outer's package. Once Outer is made final, however, it can't be subclassed, which means only classes in Outer's package can access Inner.

What still (and always) confuses me about this warning is that it applies to parameters. How is declaring a non-public parameter in a public method "exporting" it?
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:What still (and always) confuses me about this warning is that it applies to parameters. How is declaring a non-public parameter in a public method "exporting" it?

Because you're giving information about the "inner workings" of your API - even if it's only a class name - to someone who (in theory) has no business seeing it. And the fact that it makes the distinction between a final and a non-final class is actually quite clever.

However, you might be able to get away - in the case from your other thread - with leaving the class non-final, but adding a private no-args constructor - which basically makes it unextendable.

Not sure, but give it a try.

Winston
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ah, I think I get you. If I do this:



I have made method() available for anyone to call, but no one can create an instance of type Outer.Inner to pass it. The javadoc would look this:

method

public static void method(finalpublic.Outer.Inner inner)

Takes an object of type Inner and does bupkis with it. But the joke's on you, pal, because you can't create anything of type Inner, as it's not public!

Parameters:
inner - Something you can't give me.


Now, this is consistent with the NetBeans explanation of why you get a warning, which is this:

Having private or package private types in a package API is useless.

I'd be inclined to agree, since requiring a parameter of a type the user can't supply would mean the method could never be called.

Except...

Didn't we prove in my other thread that, indeed, with a non-public marker interface implemented by a public class, a client can create an instance of that class which, when calling a method requiring a parameter of the marker's type, is able to provide one, notwithstanding that the client never knows it has an instance of the marker's type? I'd hardly call that useless, as it reduced my 18 different overloaded method calls to just two (couldn't get down to one, as in your example, for reasons that aren't relevant here).

Which, again, has me thinking that the warning isn't actually justified here. Am I right?
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:However, you might be able to get away - in the case from your other thread - with leaving the class non-final, but adding a private no-args constructor - which basically makes it unextendable.

Not sure, but give it a try.


Yup, that works. You still get the warning, but the nested interface is no longer available by extension, even though the outer class isn't final. Interestingly, the NetBeans IDE is smart enough to tell you that you can't extend the outer class when you try, but not smart enough to realize the "exporting non-public type" warning is no longer relevant. Guess one can only expect just so much from an IDE, eh?

Whether or not I should use this in practice is hard for me to say. I admit, I can recall Stephan's warnings about visibility tricks leading to "black voodoo" code, and think his wisdom might apply here. I'm willing to go with the marker interfaces and a lot of documentation as a legitimate option. Using a private nullary constructor to effectively render a public class final, without upsetting the IDE... I don't know if I'm writer enough to create the documentation that would justify that trick.
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:Yup, that works. You still get the warning...

Dang! These compilers are clever, goddammit. And it's the compiler that's generating these warnings; not the IDE.

So, you're no better off than you were with final then ... in which case I'd leave it in.

but not smart enough to realize the "exporting non-public type" warning is no longer relevant.

No, no no. That warning is definitely relevant, and it suggests to me that perhaps you're trying too hard to "protect" your API. Going back to your interfaces, you can still make them public, but just put something like "has no meaning for any class implemented outside this package" in their docs. Your connect() method (from the other thread) can still throw a ClassCastException (or IllegalArumentException) if someone tries to supply a spurious Connector.From/To object.

Winston
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:Dang! These compilers are clever, goddammit. And it's the compiler that's generating these warnings; not the IDE.


Yes, and no, sorta: the compiler generates the warning, but that text about non-public types being useless in a public API is in NetBeans itself (you can even change it, I think).

That warning is definitely relevant, and it suggests to me that perhaps you're trying too hard to "protect" your API. Going back to your interfaces, you can still make them public, but just put something like "has no meaning for any class implemented outside this package" in their docs.


Yeah, I agree. Further, if I don't make the interface public, the client programmer will have no (easy) way to find out which classes implement the marking interface (unless I loosen up the javadoc generator to include non-public classes, and I ain't doin' that), because the non-public interface identified as the parameter type won't have any documentation (and, thus, there won't be any list of classes that implement it). But, if I make the marking interface public, I get the full benefit of your suggestion, because I still keep my number of overloaded connect methods to a minimum, I still get compile-time type-checking, and the client programmer can find the list of classes that implement the allowed parameter types. The only downside is that I must document clearly that client programmers are highly unlikely ever to need to implement the marking interfaces (and that's a notation one sees in OO docs a lot, so I'm comfortable producing some docs of my own that have it).

Thanks! This has been a big step forward in my understanding of what it means to expose a type, and how one should go about deciding what to make public and what not.
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:Thanks! This has been a big step forward in my understanding of what it means to expose a type, and how one should go about deciding what to make public and what not.

You're most welcome. I hope you also see that WhatNotHow can apply to design too. Initially, you were trying to work out how you could define a method with a limited combination of parameter types, whereas in fact the problem (the 'what') was how to define a type as a "valid source" and/or a "valid target" for a connection.

The solution we've come up with perhaps isn't quite the same as for a multiple-inheritance language like C++, but I think you'd agree that it solves the problem better than overloading does.

There are probably a few other things worth remembering too:
1. Hierarchies - especially single-inheritance ones - and generics can't solve every type-based problem you'll ever run into, but they can usually get you pretty close. Marker interfaces aren't ideal, but I think in this case they fit the problem as closely as we can get for the moment. However, it's very possible that there's another solution out there - maybe based on a design pattern - that fits it better; and if you discover one, don't be afraid to refactor what we've done here (and I hope you let me know what it is ).

2. Try to eliminate all compiler warnings. This is a lesser-known piece of advice from Joshua Bloch, but one I've found very useful. And if possible, don't just "cover them up" with annotations or gimmicky workarounds, because quite often they're a symptom of something more fundamental. In your case, it was trying to hide those 'markers' from your clients; but I've also found (especially with generics) that they're often a result of me "overthinking" a problem.

3. Documentation is really important. Hopefully it goes without saying, but an example like this highlights it very nicely.

Hope it helps, and best of luck with your Connectors.

Winston
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:2. Try to eliminate all compiler warnings. This is a lesser-known piece of advice from Joshua Bloch, but one I've found very useful. And if possible, don't just "cover them up" with annotations or gimmicky workarounds, because quite often they're a symptom of something more fundamental. In your case, it was trying to hide those 'markers' from your clients; but I've also found (especially with generics) that they're often a result of me "overthinking" a problem.


Agreed. Back in my early Unix days (we're talking the '80s, here), I was notorious with my co-workers for insisting that no code should be considered "done" until it compiled without warnings. Indeed, if you remember the old "lint" bug-detector, I sought to make all my code run through that without warnings. My colleagues insisted that warnings were helpful to beginners, but tended only to hold back The True Software Gods (meaning, of course, themselves). Oddly, my own mundane code, being written by a humble mortal man, tended to run without crashing, whereas The Code of The Gods tended to almost turn lead into gold, almost bring peace to the world, and almost cure cancer, except that it crashed one or two instructions before quite doing any of those things.

I'm still writing code, while I believe most of The Gods have gone on to become professors of computer science. Probably a lesson in that, somewhere...
 
Winston Gutkowski
Bartender
Posts: 10573
65
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:I'm still writing code, while I believe most of The Gods have gone on to become professors of computer science. Probably a lesson in that, somewhere...

Those who can, do... ?

Winston
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!