There are two other questions I've posted that dealt with specific cases of this:
but these have kind of left me a little lost on the general case. A basic summary of what I've gotten out of the answers to these questions is as follows:
The Liskov Substitution Principle does not apply to constructors; it only applies post-construction. You can change parameter lists all you want, and you can even make matching parameters exhibit very different behaviors.
An exception to this rule is that if a callback is passed in to the constructor of both the base class and the superclass, and if it cannot be set at a later time, then the subclass's version, in normal situations, must be backwards-compatible with superclass's version. The reason for this is that, in normal situations, outside code cannot otherwise enter into a state in which it would behave in the same way.
Even though this statement doesn't necessarily contradict itself, it comes close, yet only on a halfway fine-grained point. So at this point, it's probably a good idea for me to ask about the subprinciple of LSP that deals specifically with constructors, particularly with their parameter lists.
What is the general application of LSP here?
Example
Consider the following example, which only illustrates certain specific points (this question is still about the general case though, not this example):
BasicButton:
public class BasicButton extends Sprite
{
private var m_fOnClick:Function;
private var m_fOnPress:Function;
private var m_fOnRelease:Function;
private var m_iColorPressed:uint;
private var m_iColorReleased:uint;
public function BasicButton(pColorPressed:uint, pColorReleased:uint, pOnClick:Function
= null, pOnPress:Function = null, pOnRelease:Function = null)
{
m_iColorPressed = pColorPressed;
m_iColorReleased = pColorReleased;
m_fOnClick = pOnClick;
m_fOnPress = pOnPress;
m_fOnRelease = pOnRelease;
drawBackground(pColorReleased);
}
private function drawBackground(pColor:uint):void
{
// completely fill the entire rectangular area of the button with one solid color
}
.
.
.
// when the button has been pressed:
if (m_fOnPress)
{
m_fOnPress();
}
.
.
.
}
DirectionalButton:
// This class illustrates several different points.
public final class DirectionalButton extends BasicButton
{
private var m_eDirection:int;
private var m_fOnPress:Function;
// Multiple parameters are omitted. Two are just uint configurations to decide a color
// with, and one is a callback that, if a non-null value had been included, would have
// interacted directly with outside code. Furthermore the pOnPress callback is treated
// differently throughout this class. Finally, pOnPress and pOnRelease are no longer
// optional.
public function DirectionalButton(pDirection:int, pOnPress:Function,
pOnRelase:Function)
{
m_eDirection = pDirection;
m_fOnClick = pOnClick;
// pOnPress is nullified; the superclass won't even try to call it.
// The colors are hard-coded.
super(0x858585, 0xCCCCCC, null, onPress, pOnRelease);
addArrowSprite(pDirection);
}
private function addArrowSprite(pDirection:int):void
{
// Add a new Sprite instance as a child, which will take the form of an arrow
// pointing in the specified direction, using a different color than the
// background. Whereas the you could always assume that the superclass maintained
// one color throughout at all times, this assumption will now be broken in the
// subclass. That means that, depending on how you interpret things, the
// background color specified in the superclass's constructor uses either differing
// or equivalent behavior.
}
private function onPress():void
{
m_fOnPress(m_eDirection); // pOnPress from before has had its parameter
// list interfered with. Also it is now assumed to be
// non-null.
}
}
Remember that this example is still only dealing with the constructors' parameters, not with regular public functions or anything. Furthermore you have several different so-called "properties" that are suppliable to the constructors, but which have no way to be configured afterward.
Whether dealing with these sorts of cases, or with different types of cases, what is the LSP rule about constructors, especially with their parameter lists?