2010年10月02日

[java]で、匿名クラス?

で、モヤモヤの原因、匿名クラス。
GWTとかWicketで頻用している割に、理解は漫然としていたので調査。

※ソースは後述

<Test1>

(1)匿名クラスのインスタンス初期化ブロック使用

矢野さんのカッコイイ方式ですね。

Test1 ts2 = new Test1() {{a = "instance AAA"; b = "instance BBB";}};

...これは、代入式使ってるからカッコ良くないけど(笑)

(2)元クラスのprivateにはアクセスできない

Test1 ts3 = new Test1() {{a = "instance AAA"; b = "instance BBB"; c = "instance CCC";}};

private String cにアクセスしようとすると、当然怒られる。

(3)元クラスのメソッドのオーバーライド

Test1 ts4 = new Test1() {{a = "instance AAA"; b = "instance BBB";} String getD() {return "ddd";}};

...当然に問題ない。

(4)元クラスのインスタンス初期化ブロックのオーバーライド(super)

Test1 ts5 = new Test1() {{super();}};

無理...つか元々、継承側で元の初期化ブロックのオーバーライドとか出来ないのかな。
(やりたいなら継承側の初期化ブロックなりコンストラクタで変えればすむ話か...)

(5)匿名クラスでフィールド・メソッド実装&外部からの呼出

Test1 ts6 = new Test1() {String e; {e = "instance E";} String getD() {return super.getD() + "☆" + getE();} public String getE(){ return e;}};

匿名クラスでフィールド・メソッドの実装は可能(これも当然)だけど、これを外部から呼び出すのは無理。

#もしかすると方法はあるのかもしれないけど、そんなことする必要があるなら最初から普通のクラスにするべきだろう。

<TestIF>

インターフェース、抽象クラスのメソッドの実装

どちらかというと、これが匿名クラスの存在理由の大きな理由だろうねぇ。


結論。匿名クラスとは「クラス(抽象含む)・インターフェースのお手軽な継承実装インスタンス生成機構」って所ですかね〜(←長ったらしいから今後も「匿名クラス」って言うけど)。
いちいちイベント実装全てにキチンとクラス作ってインスタンス生成してね☆とか言われたら面倒で死ねるので、ないと困るけど。まぁ「気持ち悪い」と感じる人がいるのは理解できますね〜。

自分?一気にインスタンスまで生成してくれると思えばキモさは消えました。今後もバンバン使います(笑)

思うに「匿名クラス」って響きから「クラスが」動くような気がしてくるから気持ち悪さが残るのではないかなぁ。(アタマではインスタンスに決まってる!と思っても、Hoge h = new Hoge() {(...クラス実装...)}ってソースを見ながら「匿名クラス」って言われると...)

<Test1>

public class Test1 {
public String a = "a";
public String b = "b";
private String c = "c";
String d = "d";

String getD() {
return d;
}

public static void main(String[] args) {
// (1)匿名クラスのインスタンス初期化ブロック使用
System.out.println("--- ts2 ---");
Test1 ts2 = new Test1() {{a = "instance AAA"; b = "instance BBB";}};
System.out.println(ts2.a);
System.out.println(ts2.b);
System.out.println(ts2.c);
System.out.println(ts2.d);

// (2)元クラスのprivateにはアクセスできない
System.out.println("--- ts3 ---");
//Test1 ts3 = new Test1() {{a = "instance AAA"; b = "instance BBB"; c = "instance CCC";}};
//System.out.println(ts2.a);
//System.out.println(ts2.b);

// (3)元クラスのメソッドのオーバーライド
System.out.println("--- ts4 ---");
Test1 ts4 = new Test1() {{a = "instance AAA"; b = "instance BBB";} String getD() {return "ddd";}};
System.out.println(ts4.a);
System.out.println(ts4.b);
System.out.println(ts4.c);
System.out.println(ts4.d);
System.out.println(ts4.getD());

// (4)元クラスのインスタンス初期化ブロックのオーバーライド(super)
System.out.println("--- ts5 ---");
/*
Test1 ts5 = new Test1() {{super();}};
System.out.println(ts5.a);
System.out.println(ts5.b);
System.out.println(ts5.c);
System.out.println(ts5.d);
System.out.println(ts5.getD());
*/

// (5)匿名クラスでフィールド・メソッド実装&外部からの呼出
System.out.println("--- ts6 ---");
Test1 ts6 = new Test1() {
String e;
{
e = "instance E";
}

String getD() {
return super.getD() + "☆" + getE();
}

public String getE(){
return e;
}
};
System.out.println(ts6.a);
System.out.println(ts6.b);
System.out.println(ts6.c);
System.out.println(ts6.d);
System.out.println(ts6.getD());
//System.out.println(ts6.getE());
System.out.println(ts6.getClass());
//System.out.println(((TestStatic$3)ts6).getE());
}
}




<TestIF>

public class TestIF {
interface HogeIF {
String hogetter(String hoge);
}

abstract class HogeAbst {
abstract String hogett(String hoge);
}

public static void main(String[] args) {
// IFの匿名クラス
System.out.println("--- IFの匿名クラス ---");
HogeIF hif = new HogeIF() {
public String hogetter(String hoge) {
return hoge + "☆HogeIF";
}
};
System.out.println(hif.hogetter("ほげった〜"));

// 抽象クラスの匿名クラス
System.out.println("--- 抽象クラスの匿名クラス ---");
HogeAbst habst = new TestIF().new HogeAbst() {
String hogett(String hoge) {
return hoge + "☆HogeAbst";
}
};
System.out.println(habst.hogett("ほげっと"));
}

}


タグ:java
posted by ありい at 19:54| Comment(0) | TrackBack(0) | java | このブログの読者になる | 更新情報をチェックする

[java]匿名クラス...の前に初期化ブロックのまとめ

javaの匿名クラスの理解が曖昧で、モヤモヤしていたので調査。
主に矢野勉さんのページを参考に実験。で、匿名クラスの前に初期化ブロックの初期化順序のメモ。

(1)static変数の代入
(2)static初期化ブロック
(3)instance変数の代入
(4)instance初期化ブロック
(5)コンストラクタ


public class Test1 {
// (1)staticとして[1]
static public String sa = "sa";
static public String sb = "sb";

// (2)staticとして[2]
static {
sa = "static A";
//sb = "static B";
}

// (3)instanceとして[1]
public String a = "a";
public String b = "b";
private String c = "c";
String d = "d";

// (4)instanceとして[2]
{
a = "instance A";
//b = "instance B";
c = "instance C";
//d = "instance D";
}

// (5)instanceとして[3]
Test1() {
//a = "instance AA";
b = "instance BB";
c = "instance CC";
}

public static void main(String[] args) {
System.out.println("--- static ---");
System.out.println(sa);
System.out.println(sb);

// 普通のinstanceとして
System.out.println("--- ts1 ---");
Test1 ts1 = new Test1();
System.out.println(ts1.a);
System.out.println(ts1.b);
System.out.println(ts1.c);
System.out.println(ts1.d);
}
}

タグ:java
posted by ありい at 18:35| Comment(0) | TrackBack(0) | java | このブログの読者になる | 更新情報をチェックする

2010年09月04日

[Delphi 7]画像抜けしないQuickReport用TQRImage互換(条件あり)コンポ

今更Delphi7?(^^;
昨日Delphi XEが発売されたがな〜!という話は置いといて(^^;

仕事上、どうしても必要で調査&作成。

○手法
 1)VCLのDrawを回避、自力描画
  「中村の里」 http://www.asahi-net.or.jp/~HA3T-NKMR/tips004.htm がベースです。
 2)Delphiのメモリ管理を回避
  SetLengthをGlobalAlloc(API)に変更
  Delphi-ML http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/browse.cgi?index=063699&back=http%3A%2F%2Fw3%2Esfdata%2Ene%2Ejp%2FML%2FCB%2Fmsg25648%2Ehtml がベースです。

 どちらも中村さんの調査と発表がなければ到底回避できませんでした。本当に感謝です m(__)m

○前提・制限
 ・Delphi7&添付のQuickReportでしか試していません。
 ・2000/XPで動作確認。
 ・Metafileは非対応(元の処理に丸投げ)
 ・『画像が抜けない』は飽くまで当方での調査結果からの話であり、皆様の所でも
必ず同じ状況が起こる、または起こらない事を保証するものではありません。
 ・無保証・自己責任でお使い下さい(お約束)
 ・商用/非商用問わず、ソースの改変などご自由に。

---------------------------------
ダウンロード: HogeQRImage.pas
---------------------------------

// 適当な名前に置換して下さい
unit HogeQRImage;

interface

uses
Windows, Classes, Graphics, JPEG, Math, SysUtils, Dialogs, Forms,
qrctrls, quickrpt, qrprntr, SyncObjs;

// Cardinalで戻して欲しいので自己定義
function KStretchDIBits(DC: HDC; DestX, DestY, DestWidth, DestHeight, SrcX,
SrcY, SrcWidth, SrcHeight: Integer; Bits: Pointer; var BitsInfo: TBitmapInfo;
Usage: UINT; Rop: DWORD): Cardinal; stdcall; external gdi32 name 'StretchDIBits';

type
// 適当な名前に置換して下さい
THogeQRImage = class(TQRImage)
protected
procedure Print(OfsX, OfsY : integer); override;
end;

procedure Register;

implementation

uses StrUtils;

var
CRITICAL_SECTION: TCriticalSection;

procedure Register;
begin
RegisterComponents('QReport', [THogeQRImage]); // 適当な名前に置換して下さい
end;

// ここで調査記録
procedure WriteLog(sLog: string);
begin
// 必要があればロジックを埋めて下さい
end;

// APIエラーの内容取得(汎用)
function GetAPIErrorMessage(caError: cardinal): string;
const
MAX_BUF = 1024;
var
buf: PChar;
begin
result := '[' + CurrToStr(caError) + ']';
Buf := AllocMem(MAX_BUF);
try
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, caError, 0, buf, MAX_BUF, nil);
result := result + buf;
finally
FreeMem(Buf);
end;
end;

// 最終APIエラーの内容取得
function GetLastAPIErrorMes(): string;
begin
result := GetAPIErrorMessage(GetLastError);
end;

// 中村さんTipsから
// API結果判定(失敗時、例外を投げるように) 2010.06.03 Edit(ARI)
procedure StretchDrawBitmap(Canvas:TCanvas; // 描画先キャンバス
r : TRect; // 描画先範囲
Bitmap:TBitmap); // ビットマップ
const
InfoSize = SizeOf(TBitmapInfoHeader) + 4 * 256;
var
OldMode : integer; // StretchModeの保存用
pInfo : PBitmapInfo; // DIBヘッダ+カラーテーブルへのポインタ

InfoData : array[0..InfoSize-1] of Byte; // DIBヘッダ+カラーテーブル
Image : PByte; // DIBのピクセルデータ
DC : HDC; // GetDIBits 用 Device Context
OldPal : HPALETTE; // パレット保存用

ret: cardinal;
nSize: integer;
begin
SetLastError(0);

pInfo :=@InfoData;

// 24 Bit DIB の領域を確保
nSize := ((Bitmap.Width * 24 + 31) div 32) * 4 * Bitmap.Height;
Image := PByte(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT, nSize));

if Image = nil then begin
// メモリ確保失敗、再チャレンジ
Image := PByte(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT, nSize));
if Image = nil then begin
// 再チャレンジも失敗
raise Exception.Create('GlobalAlloc = nil : ' + GetLastAPIErrorMes());
end;
end;

try
// DIB のBitmapInfoHeader を初期化
with pInfo^.bmiHeader do begin
biSize := SizeOf(TBitmapInfoHeader);
biWidth := Bitmap.Width; biHeight := Bitmap.Height;
biPlanes := 1; biBitCount := 24;
biCompression := BI_RGB;
end;

// 24bpp DIB イメージを取得
DC := GetDC(0);
if DC = 0 then begin
// 失敗
raise Exception.Create('GetDC = nil : ' + GetLastAPIErrorMes());
end;

try
OldPal := 0;
if Bitmap.Palette <> 0 then begin
OldPal := SelectPalette(DC, Bitmap.Palette, True);
if OldPal = 0 then begin
// 失敗
raise Exception.Create('SelectPalette = nil : ' + GetLastAPIErrorMes());
end;
end;

if GetDIBits(DC, Bitmap.Handle, 0, Bitmap.Height,
Image, pInfo^, DIB_RGB_COLORS) = 0 then begin
// 失敗
raise Exception.Create('GetDIBits = 0 : ' + GetLastAPIErrorMes());
end;

if OldPal <> 0 then SelectPalette(DC, OldPal, True);
finally
ReleaseDC(0, DC);
end;

// StretchDIBits
// ※SetStretchBltModeはStretchDIBitsには不要と思われる。当方では問題は起きていない。
ret := KStretchDIBits(Canvas.Handle,
r.Left,r.Top,r.Right-r.Left,r.Bottom-r.Top,
0,0,pInfo^.bmiHeader.biWidth,pInfo^.bmiHeader.biHeight,
Image,pInfo^,DIB_RGB_COLORS,SRCCOPY);

if (ret = 0) or (ret = GDI_ERROR) then begin
// 失敗
raise Exception.Create('KStretchDIBits = ' + CurrToStr(ret) + ' : ' + GetLastAPIErrorMes());
end;

finally
GlobalFree(THandle(Image));
end;
end;

procedure THogeQRImage.Print(OfsX, OfsY: integer);
const
MAX_RETRY: integer = 10;
SLEEP_SHORT: integer = 20;
var
Dest : TRect;
bmp: TBitmap;
DC, SavedDC : THandle;

bPreview: boolean;
bPrepare: boolean;
sStatus: string;

nRetry: integer;
caPreError: cardinal;

procedure AssignBmp();
begin
if Picture.Graphic is TBitmap then begin
bmp.Assign(Picture.Bitmap);
end
else begin
bmp.Assign(Picture.Graphic);
end;

// 24bitにする
bmp.PixelFormat := pf24Bit;
end;

begin
CRITICAL_SECTION.Enter;
try
// ここまでのエラーコード
caPreError := GetLastError();

if Picture.Graphic is TMetafile then begin
// TMetafileは、わからんので元の処理に丸投げして終了
inherited Print(OfsX,OfsY);
exit;
end;

if Picture.Graphic = nil then begin
// 元画像がnilの場合、わからんので元の処理に丸投げして終了
inherited Print(OfsX,OfsY);
exit;
end;

if Picture.Graphic.Empty then begin
// 元画像が空の場合、わからんので元の処理に丸投げして終了
// 全面白の画像はEmpty扱いになるようだ
inherited Print(OfsX,OfsY);
exit;
end;

bPreview := ParentReport.QRPrinter.ShowingPreview;
bPrepare := (not bPreview) and (ParentReport.QRPrinter.Destination = qrdMetafile);

if bPreview then begin
sStatus := '[Preview]';
end
else if bPrepare then begin
sStatus := '[Prepare]';
end
else begin
sStatus := '[Print]';
end;

if (not AutoSize) and bPrepare then begin
// わざわざ後の処理をやる必要はない
// ※...と思っている。
// ※当方では問題は起きていないが不安な人は、このブロック取り払って下さい。
exit;
end;

bmp := TBitmap.Create;
try
Dest.Top := QRPrinter.YPos(OfsY + Size.Top);
Dest.Left := QRPrinter.XPos(OfsX + Size.Left);
Dest.Right := QRPrinter.XPos(OfsX + Size.Width + Size.Left);
Dest.Bottom := QRPrinter.YPos(OfsY + Size.Height + Size.Top);

// とりあえずBitmapにする
AssignBmp();

if bmp.Empty then begin
WriteLog('!bmp.Empty = true');
end;

// ※以下の繰り返し処理は、リトライで何とか復旧を試みていた頃の名残り。
// ※現在はリトライのお世話にはなっていないけど、わざわざ消す程でもないので残している。
// ※不要と思われる方は(ログ取りなど含めて)削除して下さい。

if Stretch then begin
nRetry := 0;

while nRetry <= MAX_RETRY do begin
try
StretchDrawBitmap(QRPrinter.Canvas, Dest, bmp);
break;
except
on E: Exception do begin
Inc(nRetry);
WriteLog(IntToStr(nRetry) + '回目失敗 - ' + sStatus + E.Message);
Application.ProcessMessages;
Sleep(SLEEP_SHORT);

if (nRetry mod 2) = 0 then begin
// 偶数回の失敗時、bmpの再生成
bmp.Free;
bmp := TBitmap.Create;
AssignBmp();
end;

if nRetry = (MAX_RETRY + 1) then begin
// 上限到達
WriteLog('Print前 - ' + GetAPIErrorMessage(caPreError));
end;
end;
end;
end;
end
else begin
IntersectClipRect(QRPrinter.Canvas.Handle, Dest.Left, Dest.Top, Dest.Right, Dest.Bottom);
DC := GetDC(QRPrinter.Canvas.Handle);
SavedDC := SaveDC(DC);
try
Dest.Right := Dest.Left +
round(Picture.Width / Screen.PixelsPerInch * 254 * ParentReport.QRPrinter.XFactor);
Dest.Bottom := Dest.Top +
round(Picture.Height / Screen.PixelsPerInch * 254 * ParentReport.QRPrinter.YFactor);
if Center then OffsetRect(Dest, (QRPrinter.XSize(Size.Width) -
round(Picture.Width / Screen.PixelsPerInch * 254 * ParentReport.QRPrinter.XFactor)) div 2,
(QRPrinter.YSize(Size.Height) -
round(Picture.Height / Screen.PixelsPerInch * 254 * ParentReport.QRPrinter.YFactor)) div 2);

nRetry := 0;

while nRetry <= MAX_RETRY do begin
try
StretchDrawBitmap(QRPrinter.Canvas, Dest, bmp);
break;
except
on E: Exception do begin
Inc(nRetry);
WriteLog(IntToStr(nRetry) + '回目失敗 - ' + sStatus + E.Message);
Application.ProcessMessages;
Sleep(SLEEP_SHORT);

if (nRetry mod 2) = 0 then begin
// 偶数回の失敗時、bmpの再生成
bmp.Free;
bmp := TBitmap.Create;
AssignBmp();
end;

if nRetry = (MAX_RETRY + 1) then begin
// 上限到達
WriteLog('Print前 - ' + GetAPIErrorMessage(caPreError));
end;
end;
end;
end;

finally
RestoreDC(DC, SavedDC);
SelectClipRgn(QRPrinter.Canvas.Handle, 0);
end;
end;

finally
bmp.Free;
end;

finally
CRITICAL_SECTION.Leave;
end;
end;

initialization
CRITICAL_SECTION := TCriticalSection.Create;

finalization
CRITICAL_SECTION.Free;

end.
posted by ありい at 13:35| Comment(4) | TrackBack(0) | Delphi | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。