Why does this compile?
internal class A {
public func f() {
}
}
I expected f's "public" modifier to be disallowed because its enclosing class is internal.
One motivation for allowing this is mentioned in SE-0025: Scoped Access Level (emphasis mine):
The compiler should not warn when a broader level of access control is used within a type with more restrictive access, such as internal within a private type. This allows the owner of the type to design the access they would use were they to make the type more widely accessible. (The members still cannot be accessed outside the enclosing lexical scope because the type itself is still restricted, i.e. outside code will never encounter a value of that type.)
So, although it doesn't change the accessibility of the members, it allows developers to communicate the access level they feel a given member should have if the enclosing type had a broader access level – which could for example be useful for APIs that currently have internal types which are planned to be made public in a future release.
The Swift reference says under the Guiding Principle of Access Levels,
No entity can be defined in terms of another entity that has a lower
(more restrictive) access level.
So, I suppose that doesn't mean that an entity can't be defined in terms of another entity that has a higher access level. In fact, that would surely be necessary.
Related
Say I have something like this:
#Module
internal class SeenModule {
#Provides
fun parameter() = Parameter()
#Provides
fun actualThingINeedToInject(parameter: Parameter) = ActualThing(parameter)
}
However, this module only really needs to provide an ActualThing object - in other words, the Parameter is only there because the very own module needs it. I don't want it to be part of the set of dependencies that can be retrieved outside of this module.
The way I'm currently doing this is by defining a custom scope as private and then marking the methods that provide dependencies which shouldn't leave the module with this scope, as well as those in the module where the provided dependencies should be injected, of course. This is a bit annoying because it prevents me from using other scopes in these methods and requires a lot of additional annotating all over the place. What is the proper way to achieve this?
Dagger doesn't really offer "private bindings" in the sense you're asking for, where Parameter would not be injectable from anywhere else. I also advise against using scope annotations for visibility, in part because the Component itself would need to be annotated with that scope annotation, so the scope annotation would simply slightly increase the hassle needed to improperly consume Parameter (and the hassle needed to create a Component that properly consumes Parameter).
I'd offer one of these three alternatives:
Reduce the visibility of Parameter as a class. If Parameter is package-private, you won't be able to refer to it from outside of that Java package, giving you the encapsulation you want.
Use "Subcomponents for Encapsulation", in which you create a subcomponent, install your Parameter (and any related bindings) in a Module bound on the subcomponent, and expose only your ActualThing on the subcomponent's interface. Your subcomponent will be injectable, but your Parameter is not; you can also write a #Provides method that returns your ActualThing from your subcomponent instance.
Grin and bear it, and just document that Parameter is an implementation detail that should not be accessed outside of certain packages. If you are providing objects to external teams who access ActualThing through your Component interface, you can simply decline to put Parameter on your public interface; if you are providing objects to internal teams they will likely have access to change your Dagger structure or access modifiers anyway. You might also ask yourself why Parameter would be useful for another team to consume, and document it as an API if there is a business reason for injecting it.
I have a question about internal access control level
Internal is default access control level in Swift
so I think all of internal access control should be removed
Is there a specific case of using internal access control explicitly in Swift?
When or How I use internal access control in Swift?
I found a case internal needs to be added explicitly:
public internal(set) var myInt = 0
Omitting the internal keyword results in a compile error.
This is particular useful in a swift package/pod. The property is exposed publicly, but only inside the package/pod, the value can be changed.
As per documentation:
Default Access Levels
All entities in your code (with a few specific
exceptions, as described later in this chapter) have a default access
level of internal if you don’t specify an explicit access level
yourself. As a result, in many cases you don’t need to specify an
explicit access level in your code.
Source: https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html
As you mentioned, using the "internal" keyword has no effect other than making it clear that a function should never be made public in the future without careful consideration. At this point using the "internal" keyword is more about documenting and commenting your code.
When use default access level internal, no need to add internal explicitly.
This means that SomeInternalClass and someInternalConstant can be written without an explicit access-level modifier, and will still have an access level of internal. However the keyword "internal" is used just for developers understanding.
class SomeInternalClass {} // implicitly internal
let someInternalConstant = 0 // implicitly internal
What storage policy will variables declared in a local function inside a class have, static or automatic?
The "storage policy" is the lifetime. It will always be automatic. Section 8.6 of IEEE 1800-2012 states
The lifetime of methods declared as part of a class type shall be automatic. It shall be illegal to declare a
class method with a static lifetime.
You can declare a class method as being static, but in that context it means something entirely different. Section 8.10 states:
A static method is different from a task with static lifetime. The former refers to the lifetime of the method
within the class, while the latter refers to the lifetime of the arguments and variables within the task.
So, a static method can be called even if no objects of that class exist.
The local or protected attribute of a class member has no effect on the storage policy. It only affects the visibility of the identifier used to access the members.
That being said, the storage policy of a class member also affects the visibility of that identifier. So both features have the ability to restrict access, but for different reasons.
For example, you can never access an automatic variable from outside the scope of where it was declared. It does not matter if that scope is a class method, or a simple task. And it does not matter what the default lifetime of that scope was.
Conversely, if you declare a static variable in a scope, you might be able to access that variable from outside that scope, but you need to consider all the other visibility rules.
Specifically in regards to member variables, is there a difference between the following in Swift 3? In both cases, Foo is accessible by all code in that same file. Same thing with the implicitly-scoped 'laa' property, which seems to contradict the documentation.
If you define a type’s access level as private or file private, the default access level of its members will also be private or file private.
However, in both cases below, 'laa' is accessible from other classes in the same file implying that it is fileprivate, not private as the docs say the first one should be.
private class Foo
{
var laa:String
}
fileprivate class Foo
{
var laa:String
}
As said in this Q&A – there's no difference in the access levels between a top-level private and fileprivate declaration. private simply means that it's accessible only in the enclosing scope1, and at the top-level – the file is that scope.
Regarding the documentation comment:
If you define a type’s access level as private or file private, the default access level of its members will also be private or file private.
I would say this is incorrect, or at the very least misleading in the case of private. The scope in which a given type's members are visible is by default the scope that the type declaration itself is visible in (with the exception of access levels higher than internal).
Therefore the scope in which a private type's members are accessible is by default the enclosing scope that defines that type. At the top level, that's the file.
It's probably simpler just to say that type members default to being internal. Being declared in a type with a lower access level than this (such as private or fileprivate) just prevents the members from being visible outside of these access levels (as it makes no sense to refer to a given type's member without being able to see the type itself).
1. Note that in Swift 4, as per SE-0169, extensions of a given type that are declared in the same source file as the type have the same access control scope as the scope of the type declaration. Therefore they can access private members of the type.
Are there any risks involved with using the protected member access specifier instead of the private member access specifier?
You have not specified which lamguage but, I assume in OO language like java, a protected member is "visible" to child classes. Latter can use them and may break if you happen to change protected members later due to evolution of your class or change in requirements. No such risk in case of private members. A child class can extend the protected members and make it exposed to the outside (public) world.
This answer assumes you are using a language like java:
private members can only be seen by its own class (and inner classes). This is the safest field visibility since it is hidden from all outsiders. Since it's completely hidden, you are free to change the implementation details or even get rid of the field completely later on.
protected members can not only be seen by all subclasses but also from ALL classes that are in the same package. This makes it much harder to later change the field because more classes might be referencing it.